iprutils/0000755000000000000000000000000012275251164011434 5ustar rootrootiprutils/iprconfig.c0000644000000000000000000147606012275251164013576 0ustar rootroot/** * IBM IPR adapter configuration utility * * (C) Copyright 2000, 2004 * International Business Machines Corporation and others. * All Rights Reserved. This program and the accompanying * materials are made available under the terms of the * Common Public License v1.0 which accompanies this distribution. **/ #ifndef iprlib_h #include "iprlib.h" #endif #include #include #include #include #include #include #include #include #include "iprconfig.h" #include #include #include #include #include #include char *tool_name = "iprconfig"; struct devs_to_init_t { struct ipr_dev *dev; struct ipr_ioa *ioa; int new_block_size; int cmplt; int do_init; int done; int dev_type; #define IPR_AF_DASD_DEVICE 1 #define IPR_JBOD_DASD_DEVICE 2 struct devs_to_init_t *next; }; #define for_each_dev_to_init(dev) for (dev = dev_init_head; dev; dev = dev->next) struct prot_level { struct ipr_array_cap_entry *array_cap_entry; u8 is_valid:1; u8 reserved:7; }; struct array_cmd_data { u8 array_id; u8 prot_level; u16 stripe_size; int qdepth; u32 do_cmd; struct ipr_ioa *ioa; struct ipr_dev *dev; struct ipr_array_query_data *qac; struct array_cmd_data *next; }; /* not needed once menus incorporated into screen_driver */ struct window_l { WINDOW *win; struct window_l *next; }; struct panel_l { PANEL *panel; struct panel_l *next; }; static struct devs_to_init_t *dev_init_head = NULL; static struct devs_to_init_t *dev_init_tail = NULL; static struct array_cmd_data *raid_cmd_head = NULL; static struct array_cmd_data *raid_cmd_tail = NULL; static i_container *i_con_head = NULL; /* FIXME requires multiple heads */ static char log_root_dir[200]; static char editor[200]; FILE *errpath; static int toggle_field; nl_catd catd; static char **add_args; static int num_add_args; static int use_curses; #define for_each_raid_cmd(cmd) for (cmd = raid_cmd_head; cmd; cmd = cmd->next) #define DEFAULT_LOG_DIR "/var/log" #define DEFAULT_EDITOR "vi -R" #define FAILSAFE_EDITOR "vi -R -" #define IS_CANCEL_KEY(c) ((c == KEY_F(12)) || (c == 'q') || (c == 'Q')) #define CANCEL_KEY_LABEL "q=Cancel " #define IS_EXIT_KEY(c) ((c == KEY_F(3)) || (c == 'e') || (c == 'E')) #define EXIT_KEY_LABEL "e=Exit " #define CONFIRM_KEY_LABEL "c=Confirm " #define CONFIRM_REC_KEY_LABEL "s=Confirm Reclaim " #define ENTER_KEY '\n' #define IS_ENTER_KEY(c) ((c == KEY_ENTER) || (c == ENTER_KEY)) #define IS_REFRESH_KEY(c) ((c == 'r') || (c == 'R')) #define REFRESH_KEY_LABEL "r=Refresh " #define IS_TOGGLE_KEY(c) ((c == 't') || (c == 'T')) #define TOGGLE_KEY_LABEL "t=Toggle " #define IS_PGDN_KEY(c) ((c == KEY_NPAGE) || (c == 'f') || (c == 'F')) #define PAGE_DOWN_KEY_LABEL "f=PageDn " #define IS_PGUP_KEY(c) ((c == KEY_PPAGE) || (c == 'b') || (c == 'B')) #define PAGE_UP_KEY_LABEL "b=PageUp " #define IS_5250_CHAR(c) (c == 0xa) #define is_digit(ch) (((ch)>=(unsigned)'0'&&(ch)<=(unsigned)'9')?1:0) struct special_status { int index; int num; char *str; } s_status; char *print_device(struct ipr_dev *, char *, char *, int); int display_menu(ITEM **, int, int, int **); char *__print_device(struct ipr_dev *, char *, char *, int, int, int, int, int, int, int, int, int, int, int, int); static char *print_path_details(struct ipr_dev *, char *); static int get_drive_phy_loc(struct ipr_ioa *ioa); #define print_dev(i, dev, buf, fmt, type) \ for (i = 0; i < 2; i++) \ (buf)[i] = print_device(dev, (buf)[i], fmt, type); #define print_dev_conc(i, dev, buf, fmt, type) \ for (i = 0; i < 3; i++) \ (buf)[i] = print_device(dev, (buf)[i], fmt, (type + 5)); #define print_dev_enclosure(i, dev, buf, fmt, type) \ for (i = 0; i < 2; i++) { \ if ( i == 0 ) \ (buf)[i] = print_device(dev, (buf)[i], fmt, (type + 8)); \ if (i == 1) \ (buf)[i] = print_device(dev, (buf)[i], fmt, (type + 8)); \ } static struct sysfs_dev *head_sdev; static struct sysfs_dev *tail_sdev; /** * is_format_allowed - * @dev: ipr dev struct * * Returns: * 1 if format is allowed / 0 otherwise **/ static int is_format_allowed(struct ipr_dev *dev) { int rc; struct ipr_cmd_status cmd_status; struct ipr_cmd_status_record *status_record; struct sense_data_t sense_data; if (ipr_is_af_dasd_device(dev)) { rc = ipr_query_command_status(dev, &cmd_status); if (rc == 0 && cmd_status.num_records != 0) { status_record = cmd_status.record; if ((status_record->status != IPR_CMD_STATUS_SUCCESSFUL) && (status_record->status != IPR_CMD_STATUS_FAILED)) return 0; } } else { rc = ipr_test_unit_ready(dev, &sense_data); if (rc == CHECK_CONDITION && (sense_data.error_code & 0x7F) == 0x70) { if (sense_data.add_sense_code == 0x31 && sense_data.add_sense_code_qual == 0x00) return 1; else if ((sense_data.sense_key & 0x0F) == 0x02) return 0; } } return 1; } /** * can_format_for_raid - * @dev: ipr dev struct * * Returns: * 1 if format for raid is allowed / 0 otherwise **/ static int can_format_for_raid(struct ipr_dev *dev) { if (dev->ioa->is_secondary) return 0; if (!ipr_is_gscsi(dev) && !ipr_is_af_dasd_device(dev)) return 0; if (ipr_is_hot_spare(dev) || !device_supported(dev)) return 0; if (ipr_is_array_member(dev) && !dev->dev_rcd->no_cfgte_vol) return 0; if (ipr_is_af_dasd_device(dev) && ipr_device_is_zeroed(dev)) return 0; if (!ipr_is_af_dasd_device(dev)) { /* If on a JBOD adapter */ if (!dev->ioa->qac_data->num_records) return 0; if (is_af_blocked(dev, 0)) return 0; } if (!is_format_allowed(dev)) return 0; return 1; } /** * flush_stdscr - * * Returns: * nothing **/ /* not needed after screen_driver can do menus */ static void flush_stdscr() { if (!use_curses) { fprintf(stdout, "\r"); fflush(stdout); return; } nodelay(stdscr, TRUE); while(getch() != ERR) {} nodelay(stdscr, FALSE); } /** * free_i_con - free memory from an i_container list * @i_con: i_container struct * * Returns: * NULL **/ static i_container *free_i_con(i_container *i_con) { i_container *temp_i_con; i_con = i_con_head; if (i_con == NULL) return NULL; do { temp_i_con = i_con->next_item; free(i_con); i_con = temp_i_con; } while (i_con); i_con_head = NULL; return NULL; } /** * add_i_con - create a new i_con list or add an i_container to a list * @i_con: i_container struct * @f: field data * @d: data buffer * * Returns: * i_container pointer **/ static i_container *add_i_con(i_container *i_con, char *f, void *d) { i_container *new_i_con; new_i_con = malloc(sizeof(i_container)); new_i_con->next_item = NULL; /* used to hold data entered into user-entry fields */ strncpy(new_i_con->field_data, f, MAX_FIELD_SIZE+1); new_i_con->field_data[strlen(f)+1] = '\0'; /* a pointer to the device information represented by the i_con */ new_i_con->data = d; if (i_con) i_con->next_item = new_i_con; else i_con_head = new_i_con; return new_i_con; } /** * exit_confirmed - * @i_con: i_container struct * * Returns: * 0 **/ int exit_confirmed(i_container *i_con) { exit_func(); exit(0); return 0; } /** * free_screen - * @panel: * @win: * @fields: * * Returns: * nothing **/ /*not needed after screen_driver can do menus */ static void free_screen(struct panel_l *panel, struct window_l *win, FIELD **fields) { struct panel_l *cur_panel; struct window_l *cur_win; int i; cur_panel = panel; while(cur_panel) { panel = panel->next; del_panel(cur_panel->panel); free(cur_panel); cur_panel = panel; } cur_win = win; while(cur_win) { win = win->next; delwin(cur_win->win); free(cur_win); cur_win = win; } if (fields) { for (i = 0; fields[i] != NULL; i++) free_field(fields[i]); } } /** * add_raid_cmd_tail - * @ioa: ipr ioa struct * @dev: ipr dev struct * @array_id: array ID * * Returns: * nothing **/ static void add_raid_cmd_tail(struct ipr_ioa *ioa, struct ipr_dev *dev, u8 array_id) { if (raid_cmd_head) { raid_cmd_tail->next = malloc(sizeof(struct array_cmd_data)); raid_cmd_tail = raid_cmd_tail->next; } else { raid_cmd_head = raid_cmd_tail = malloc(sizeof(struct array_cmd_data)); } memset(raid_cmd_tail, 0, sizeof(struct array_cmd_data)); raid_cmd_tail->array_id = array_id; raid_cmd_tail->ioa = ioa; raid_cmd_tail->dev = dev; } /** * free_raid_cmds - free the raid_cmd_head list * * Returns: * nothing **/ static void free_raid_cmds() { struct array_cmd_data *cur = raid_cmd_head; while(cur) { free(cur->qac); cur = cur->next; free(raid_cmd_head); raid_cmd_head = cur; } } /** * free_devs_to_init - free the dev_init_head list * * Returns: * nothing **/ static void free_devs_to_init() { struct devs_to_init_t *dev = dev_init_head; while (dev) { dev = dev->next; free(dev_init_head); dev_init_head = dev; } } /** * strip_trailing_whitespace - remove trailing white space from a string * @p_str: string * * Returns: * 0 if success / non-zero on failure **/ static char *strip_trailing_whitespace(char *p_str) { int len; char *p_tmp; len = strlen(p_str); if (len == 0) return p_str; p_tmp = p_str + len - 1; while(*p_tmp == ' ' || *p_tmp == '\n') p_tmp--; p_tmp++; *p_tmp = '\0'; return p_str; } /** * tool_exit_func - clean up curses stuff on exit if needed * * Returns: * nothing **/ static void tool_exit_func() { if (use_curses) { clearenv(); clear(); refresh(); endwin(); } } /** * cmdline_exit_func - * * Returns: * nothing **/ static void cmdline_exit_func() { } /** * ipr_list_opts - * @body: * @key: * @list_str: * * Returns: * pointer to body string **/ static char *ipr_list_opts(char *body, char *key, char *list_str) { int start_len = 0; char *string_buf = _("Select one of the following"); if (!body) { body = realloc(body, strlen(string_buf) + 8); sprintf(body, "\n%s:\n\n", string_buf); } start_len = strlen(body); body = realloc(body, start_len + strlen(key) + strlen(_(list_str)) + 16); sprintf(body + start_len, " %s. %s\n", key, _(list_str)); return body; } /** * ipr_end_list - * @body: * * Returns: * pointer to body string **/ static char *ipr_end_list(char *body) { int start_len = 0; char *string_buf = _("Selection"); start_len = strlen(body); body = realloc(body, start_len + strlen(string_buf) + 16); sprintf(body + start_len, "\n\n%s: %%1", string_buf); return body; } /** * screen_driver - Main driver for curses based display * @screen: s_node * @header_lines: number of header lines * @i_con: i_container struct * * Returns: * screen_output struct **/ static struct screen_output *screen_driver(s_node *screen, int header_lines, i_container *i_con) { WINDOW *w_pad = NULL,*w_page_header = NULL; /* windows to hold text */ FIELD **fields = NULL; /* field list for forms */ FORM *form = NULL; /* form for input fields */ int rc = 0; /* the status of the screen */ char *status; /* displays screen operations */ char buffer[100]; /* needed for special status strings */ bool invalid = false; /* invalid status display */ bool ground_cursor=false; int stdscr_max_y,stdscr_max_x; int w_pad_max_y=0,w_pad_max_x=0; /* limits of the windows */ int pad_l=0,pad_scr_r,pad_t=0,viewable_body_lines; int title_lines=2,footer_lines=2; /* position of the pad */ int center,len=0,ch; int i=0,j,k,row=0,col=0,bt_len=0; /* text positioning */ int field_width,num_fields=0; /* form positioning */ int h,w,t,l,o,b; /* form querying */ int active_field = 0; /* return to same active field after a quit */ bool pages = false,is_bottom = false; /* control page up/down in multi-page screens */ bool form_adjust = false; /* correct cursor position in multi-page screens */ bool refresh_stdscr = true; bool x_offscr,y_offscr; /* scrolling windows */ bool pagedn, fakepagedn; char *input = NULL; struct screen_output *s_out; struct screen_opts *temp; /* reference to another screen */ char *title,*body,*body_text; /* screen text */ i_container *temp_i_con; int num_i_cons = 0; int x, y; int f_flags; s_out = malloc(sizeof(struct screen_output)); /* passes an i_con and an rc value back to the function */ /* create text strings */ title = _(screen->title); body = screen->body; f_flags = screen->f_flags; if (f_flags & ENTER_FLAG) footer_lines += 3; else if (f_flags & FWD_FLAG) footer_lines++; if (f_flags & TOGGLE_FLAG) active_field = toggle_field; bt_len = strlen(body); body_text = calloc(bt_len + 1, sizeof(char)); /* determine max size needed and find where to put form fields and device * input in the text and add them * '%' marks a field. The number after '%' represents the field's width */ i=0; temp_i_con = i_con_head; while(body[i] != '\0') { if (body[i] == '\n') { row++; col = -1; body_text[len] = body[i]; len++; } else if ((body[i] == '%') && is_digit(body[i+1])) { fields = realloc(fields,sizeof(FIELD **) * (num_fields + 1)); i++; field_width = 0; sscanf(&(body[i]),"%d",&field_width); fields[num_fields] = new_field(1,field_width,row,col,0,0); if (field_width >= 30) field_opts_off(fields[num_fields],O_AUTOSKIP); if (field_width > 9) /* skip second digit of field index */ i++; if ((temp_i_con) && (temp_i_con->field_data[0]) && (f_flags & MENU_FLAG)) { set_field_buffer(fields[num_fields], 0, temp_i_con->field_data); input = field_buffer(fields[num_fields],0); temp_i_con = temp_i_con->next_item; num_i_cons++; } set_field_userptr(fields[num_fields],NULL); num_fields++; col += field_width-1; bt_len += field_width; body_text = realloc(body_text,bt_len); for(k = 0; k < field_width; k++) body_text[len+k] = ' '; len += field_width; } else { body_text[len] = body[i]; len++; } col++; i++; if (col > w_pad_max_x) w_pad_max_x = col; } body_text[len] = '\0'; w_pad_max_y = row; w_pad_max_y++; /* some forms may not display correctly if this is not included */ w_pad_max_x++; /* adds a column for return char \n : else, a blank line will appear in the pad */ w_pad = newpad(w_pad_max_y,w_pad_max_x); keypad(w_pad,TRUE); if (num_fields) { fields = realloc(fields,sizeof(FIELD **) * (num_fields + 1)); fields[num_fields] = NULL; form = new_form(fields); } /* make the form appear in the pad; not stdscr */ set_form_win(form,w_pad); set_form_sub(form,w_pad); while(1) { getmaxyx(stdscr,stdscr_max_y,stdscr_max_x); /* clear all windows before writing new text to them */ clear(); wclear(w_pad); if(pages) wclear(w_page_header); center = stdscr_max_x/2; len = strlen(title); /* add the title at the center of stdscr */ mvaddstr(0,(center-len/2)>0?center-len/2:0,title); /* set the boundaries of the pad based on the size of the screen and determine whether or not the user is able to scroll */ if ((stdscr_max_y - title_lines - footer_lines + 1) >= w_pad_max_y) { viewable_body_lines = w_pad_max_y; pad_t = 0; y_offscr = false; /*not scrollable*/ pages = false; for (i = 0; i < num_fields; i++) field_opts_on(fields[i],O_ACTIVE); } else { viewable_body_lines = stdscr_max_y - title_lines - footer_lines - 1; y_offscr = true; /*scrollable*/ if (f_flags & FWD_FLAG) { pages = true; w_page_header = subpad(w_pad,header_lines,w_pad_max_x,0,0); } } if (stdscr_max_x > w_pad_max_x) { pad_scr_r = w_pad_max_x; pad_l = 0; x_offscr = false; } else { pad_scr_r = stdscr_max_x; x_offscr = true; } move(stdscr_max_y - footer_lines,0); if (f_flags & ENTER_FLAG) { if (num_fields == 0) { addstr(_("\nPress Enter to Continue\n\n")); ground_cursor = true; } else addstr(_("\nOr leave blank and press Enter to cancel\n\n")); } if ((f_flags & FWD_FLAG) && !(f_flags & ENTER_FLAG)) addstr("\n"); if (f_flags & CONFIRM_FLAG) addstr(CONFIRM_KEY_LABEL); if (f_flags & EXIT_FLAG) addstr(EXIT_KEY_LABEL); if (f_flags & CANCEL_FLAG) addstr(CANCEL_KEY_LABEL); if (f_flags & CONFIRM_REC_FLAG) addstr(CONFIRM_REC_KEY_LABEL); if (f_flags & REFRESH_FLAG) addstr(REFRESH_KEY_LABEL); if (f_flags & TOGGLE_FLAG) addstr(TOGGLE_KEY_LABEL); if ((f_flags & FWD_FLAG) && y_offscr) { addstr(PAGE_DOWN_KEY_LABEL); addstr(PAGE_UP_KEY_LABEL); } post_form(form); /* add the body of the text to the screen */ if (pages) { wmove(w_page_header,0,0); waddstr(w_page_header,body_text); } wmove(w_pad,0,0); waddstr(w_pad,body_text); if (num_fields) { set_current_field(form,fields[active_field]); if (active_field == 0) form_driver(form,REQ_NEXT_FIELD); form_driver(form,REQ_PREV_FIELD); } refresh_stdscr = true; for(;;) { /* add global status if defined */ if (s_status.index) { rc = s_status.index; s_status.index = 0; } if (invalid) { status = (char *)screen_status[INVALID_OPTION_STATUS]; invalid = false; } else if (rc != 0) { if ((status = strchr(screen_status[rc],'%')) == NULL) { status = (char *)screen_status[rc]; } else if (status[1] == 'd') { sprintf(buffer,screen_status[rc],s_status.num); status = buffer; } else if (status[1] == 's') { sprintf(buffer,screen_status[rc],s_status.str); status = buffer; } rc = 0; } else status = NULL; move(stdscr_max_y-1,0); clrtoeol(); /* clear last line */ mvaddstr(stdscr_max_y-1,0,status); status = NULL; pagedn = false; if ((f_flags & FWD_FLAG) && y_offscr) { if (!is_bottom) mvaddstr(stdscr_max_y-footer_lines,stdscr_max_x-8,_("More...")); else mvaddstr(stdscr_max_y-footer_lines,stdscr_max_x-8," "); for (i = 0; i < num_fields; i++) { if (!fields[i]) continue; /* height, width, top, left, offscreen rows, buffer */ field_info(fields[i],&h,&w,&t,&l,&o,&b); /* turn all offscreen fields off */ if ((t >= (header_lines + pad_t)) && (t <= (viewable_body_lines + pad_t))) { field_opts_on(fields[i],O_ACTIVE); if (!form_adjust) continue; if (!(f_flags & TOGGLE_FLAG) || !toggle_field) set_current_field(form,fields[i]); form_adjust = false; } else if (field_opts_off(fields[i],O_ACTIVE) == E_CURRENT && toggle_field && i == (toggle_field - 1)) { pagedn = true; } } } if (f_flags & MENU_FLAG) { for (i = 0; i < num_fields; i++) { field_opts_off(fields[i],O_VISIBLE); field_opts_on(fields[i],O_VISIBLE); set_field_fore(fields[i],A_BOLD); } } if ((f_flags & TOGGLE_FLAG) && toggle_field && (((field_opts(fields[toggle_field-1]) & O_ACTIVE) != O_ACTIVE) || pagedn)) { pagedn = false; fakepagedn = true; ch = KEY_NPAGE; } else { fakepagedn = false; toggle_field = 0; if (refresh_stdscr) { touchwin(stdscr); refresh_stdscr = FALSE; } else touchline(stdscr,stdscr_max_y - 1,1); refresh(); if (pages) { touchwin(w_page_header); prefresh(w_page_header, 0, pad_l, title_lines, 0, title_lines + header_lines - 1, pad_scr_r - 1); touchwin(w_pad); prefresh(w_pad, pad_t + header_lines, pad_l, title_lines + header_lines, 0, title_lines + viewable_body_lines, pad_scr_r - 1); } else { touchwin(w_pad); prefresh(w_pad, pad_t, pad_l, title_lines, 0, title_lines + viewable_body_lines, pad_scr_r - 1); } if (ground_cursor) { move(stdscr_max_y - 1, stdscr_max_x - 1); refresh(); } ch = wgetch(w_pad); /* pause for user input */ } if (ch == KEY_RESIZE) break; if (IS_ENTER_KEY(ch)) { if (num_fields > 0) active_field = field_index(current_field(form)); if ((f_flags & ENTER_FLAG) && num_fields == 0) { rc = (CANCEL_FLAG | rc); goto leave; } else if (f_flags & ENTER_FLAG) { /* cancel if all fields are empty */ form_driver(form,REQ_VALIDATION); for (i = 0; i < num_fields; i++) { if (strlen(field_buffer(fields[i],0)) != 0) /* fields are not empty -> continue */ break; if (i == (num_fields - 1)) { /* all fields are empty */ rc = (CANCEL_FLAG | rc); goto leave; } } } if (num_fields > 0) input = field_buffer(fields[active_field],0); invalid = true; for (i = 0; i < screen->num_opts; i++) { temp = &(screen->options[i]); if ((temp->key[0] == '\n') || ((num_fields > 0)?(strcasecmp(input,temp->key) == 0):0)) { invalid = false; if ((temp->key[0] == '\n') && (num_fields > 0)) { /* store field data to existing i_con (which should already contain pointers) */ i_container *temp_i_con = i_con_head; form_driver(form,REQ_VALIDATION); for (i = 0; i < num_fields; i++) { strncpy(temp_i_con->field_data,field_buffer(fields[i],0),MAX_FIELD_SIZE); temp_i_con = temp_i_con->next_item; } } if (temp->screen_function == NULL) /* continue with function */ goto leave; toggle_field = 0; do rc = temp->screen_function(i_con_head); while (rc == REFRESH_SCREEN || rc & REFRESH_FLAG); /* if screen flags exist on rc and they don't match the screen's flags, return */ if ((rc & 0xF000) && !(screen->rc_flags & (rc & 0xF000))) goto leave; /* strip screen flags from rc */ rc &= ~(EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG); if (screen->rc_flags & REFRESH_FLAG) { s_status.index = rc; rc = (REFRESH_FLAG | rc); goto leave; } break; } } if (!invalid) { /*clear fields*/ form_driver(form,REQ_LAST_FIELD); for (i = 0; i < num_fields; i++) { form_driver(form,REQ_CLR_FIELD); form_driver(form,REQ_NEXT_FIELD); } break; } } else if (IS_EXIT_KEY(ch) && (f_flags & EXIT_FLAG)) { rc = (EXIT_FLAG | rc); goto leave; } else if (IS_CANCEL_KEY(ch) && ((f_flags & CANCEL_FLAG) || screen == &n_main_menu)) { rc = (CANCEL_FLAG | rc); goto leave; } else if ((IS_REFRESH_KEY(ch) && (f_flags & REFRESH_FLAG)) || (ch == KEY_HOME && (f_flags & REFRESH_FLAG))) { rc = REFRESH_SCREEN; if (num_fields > 1) toggle_field = field_index(current_field(form)) + 1; goto leave; } else if (IS_TOGGLE_KEY(ch) && (f_flags & TOGGLE_FLAG)) { rc = TOGGLE_SCREEN; if (num_fields > 1) toggle_field = field_index(current_field(form)) + 1; goto leave; } else if (IS_PGUP_KEY(ch) && (f_flags & FWD_FLAG) && y_offscr) { invalid = false; if (pad_t > 0) { pad_t -= (viewable_body_lines - header_lines + 1); rc = PGUP_STATUS; } else if ((f_flags & FWD_FLAG) && y_offscr) rc = TOP_STATUS; else invalid = true; if (!invalid) { if (pad_t < 0) pad_t = 0; is_bottom = false; form_adjust = true; refresh_stdscr = true; } } else if (IS_PGDN_KEY(ch) && (f_flags & FWD_FLAG) && y_offscr) { invalid = false; if ((stdscr_max_y - title_lines - footer_lines + 1) < (w_pad_max_y - pad_t)) { pad_t += (viewable_body_lines - header_lines + 1); rc = PGDN_STATUS; if ((stdscr_max_y - title_lines - footer_lines + 1) >= (w_pad_max_y - pad_t)) is_bottom = true; } else if (is_bottom) rc = BTM_STATUS; else invalid = true; if (fakepagedn) rc = 0; if (!invalid) { form_adjust = true; refresh_stdscr = true; } } else if (ch == KEY_RIGHT) { if (x_offscr && stdscr_max_x+pad_l0) pad_l--; else form_driver(form, REQ_PREV_CHAR); } else if (ch == KEY_UP) { if ((y_offscr || (num_fields == 0)) && !pages) { if (pad_t > 0) { pad_t--; form_adjust = true; is_bottom = false; } } else form_driver(form, REQ_PREV_FIELD); } else if (ch == KEY_DOWN) { if ((y_offscr || (num_fields == 0)) && !pages) { if (y_offscr && ((stdscr_max_y + pad_t ) < (w_pad_max_y + title_lines + footer_lines))) { pad_t++; form_adjust = true; } if (!((stdscr_max_y + pad_t) < (w_pad_max_y + title_lines + footer_lines))) is_bottom = true; } else form_driver(form, REQ_NEXT_FIELD); } else if ((ch == KEY_BACKSPACE) || (ch == 127)) form_driver(form, REQ_DEL_PREV); else if (ch == KEY_DC) form_driver(form, REQ_DEL_CHAR); else if (ch == KEY_IC) form_driver(form, REQ_INS_MODE); else if (ch == KEY_EIC) form_driver(form, REQ_OVL_MODE); else if (ch == KEY_HOME) form_driver(form, REQ_BEG_FIELD); else if (ch == KEY_END) form_driver(form, REQ_END_FIELD); else if (ch == '\t') form_driver(form, REQ_NEXT_FIELD); else if ((f_flags & MENU_FLAG) || (num_fields == 0)) { invalid = true; for (i = 0; i < screen->num_opts; i++) { temp = &(screen->options[i]); if ((temp->key) && (ch == temp->key[0])) { invalid = false; if (num_fields > 0) { active_field = field_index(current_field(form)); input = field_buffer(fields[active_field],0); if (active_field < num_i_cons) { temp_i_con = i_con_head; for (j = 0; j < active_field; j++) temp_i_con = temp_i_con->next_item; temp_i_con->field_data[0] = '\0'; getyx(w_pad,y,x); temp_i_con->y = y; temp_i_con->x = x; } toggle_field = field_index(current_field(form)) + 1; } if (temp->screen_function == NULL) goto leave; do rc = temp->screen_function(i_con_head); while (rc == REFRESH_SCREEN || rc & REFRESH_FLAG); if ((rc & 0xF000) && !(screen->rc_flags & (rc & 0xF000))) goto leave; rc &= ~(EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG); if (screen->rc_flags & REFRESH_FLAG) { rc = (REFRESH_FLAG | rc); goto leave; } break; } } } else if (isascii(ch)) form_driver(form, ch); } } leave: s_out->i_con = i_con_head; s_out->rc = rc; free(body_text); unpost_form(form); free_form(form); for (i = 0; i < num_fields; i++) { if (fields[i] != NULL) free_field(fields[i]); } free(fields); delwin(w_pad); delwin(w_page_header); return s_out; } static char *status_hdr[] = { /* . . . . . */ /*012345678901234567890123456789012345678901234567890123456789012345678901234567890 */ "OPT Name Resource Path/Address Vendor Product ID Status", "OPT Name PCI/SCSI Location Description Status", "Name Resource Path/Address Vendor Product ID Status", "Name PCI/SCSI Location Description Status", "OPT SAS Port/SAS Address Description Active Status Info", "OPT SAS Port/SAS Address Description Active Status Info", "SAS Port/SAS Address Description Active Status Info", "SAS Port/SAS Address Description Active Status Info", "OPT Name Platform Location Description Status", "OPT Name SCSI Host/Resource Path Vendor Product ID Status", "OPT Name SCSI Host/Resource Path Vendor Product ID Status", "Name Platform Location Description Status", "OPT Name PCI/Host/Resource Path Serial Number Status", "OPT Name Physical Location Production ID Status", "Name Physical Location Serial Number Status"}; static char *status_sep[] = { "--- ------ -------------------------- -------- ---------------- -----------------", "--- ------ ------------------------- ------------------------- -----------------", "------ -------------------------- -------- ---------------- -----------------", "------ ------------------------- ------------------------- -----------------", "--- --------------------- ------------------ ------ ----------------- ----------", "--- ---------------------- ------------------ ------ ----------------- ----------", "---------------------- ------------------ ------ ----------------- ----------", "---------------------- ------------------ ------ ----------------- ----------", "--- ------ -------------------------- ------------------------- -----------------", "--- ------ ---------------------------- -------- ---------------- --------------", "--- ------ ---------------------------- -------- ---------------- --------------", "------ -------------------------- ------------------------- ------------", "--- ------ ---------------------------------------- ------------- ------------", "--- ------ ---------------------------------------- ---------------- ------------", "------ ---------------------------------------- ------------- ------------", }; /** * status_header - * @buffer: data buffer * @num_lines: number of lines * @type: type index * * Returns: * buffer **/ static char *status_header(char *buffer, int *num_lines, int type) { int cur_len = strlen(buffer); int header_lines = 0; buffer = realloc(buffer, cur_len + strlen(status_hdr[type]) + strlen(status_sep[type]) + 8); cur_len += sprintf(buffer + cur_len, "%s\n", status_hdr[type]); cur_len += sprintf(buffer + cur_len, "%s\n", status_sep[type]); header_lines += 2; *num_lines = header_lines + *num_lines; return buffer; } /** * add_string_to_body - adds a string of data to fit in the text window, * putting carraige returns in when necessary * @body: * @new_text: * @line_fill: * @line_num: * * Returns: * body on success / 0 if no space available **/ /* NOTE: This routine expects the language conversion to be done before entering this routine */ static char *add_string_to_body(char *body, char *new_text, char *line_fill, int *line_num) { int body_len = 0; int new_text_len = strlen(new_text); int line_fill_len = strlen(line_fill); int rem_length; int new_text_offset = 0; int max_y, max_x; int len, i; int num_lines = 0; struct winsize w; if (body) body_len = strlen(body); len = body_len; if (use_curses) getmaxyx(stdscr,max_y,max_x); else { memset(&w, 0, sizeof(struct winsize)); ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); if (w.ws_col > 0) max_x = w.ws_col; else max_x = 80; } rem_length = max_x - 1; for (i = 0; i < strlen(new_text); i++) { if (new_text[i] == '\n') num_lines++; } while (len != 0) { if (body[len--] == '\n') break; rem_length--; } while (1) { if (new_text_len < rem_length) { /* done, all data fits in current line */ body = realloc(body, body_len + new_text_len + 8); sprintf(body + body_len, "%s", &new_text[new_text_offset]); break; } len = rem_length; while (len != 0) { if (new_text[new_text_offset + len] == ' ') { /* found a place to \n */ break; } len--; } if (len == 0) /* no space to fit any word */ return 0; /* adjust len to compensate 0 based array */ len += 1; body = realloc(body, body_len + len + line_fill_len + 8); strncpy(body + body_len, new_text + new_text_offset, len); body_len += len; new_text_offset += len; new_text_len -= len; body_len += sprintf(body + body_len, "\n%s", line_fill); rem_length = max_x - 1 - line_fill_len; num_lines++; } if (line_num) *line_num = *line_num + num_lines; return body; } /** * add_line_to_body - adds a single line to the body text of the screen, it is * intented primarily to list items with the desciptor on the * left of the screen and ". . . :" to the data field for the * descriptor. * @body: * @new_text: * @field_data: * * Returns: * body **/ /* NOTE: This routine expects the language conversion to be done before entering this routine */ #define DETAILS_DESCR_LEN 40 static char *add_line_to_body(char *body, char *new_text, char *field_data) { int cur_len = 0, start_len = 0; int str_len, field_data_len; int i; if (body != NULL) start_len = cur_len = strlen(body); str_len = strlen(new_text); if (!field_data) { body = realloc(body, cur_len + str_len + 8); sprintf(body + cur_len, "%s\n", new_text); } else { field_data_len = strlen(field_data); if (str_len < DETAILS_DESCR_LEN) { body = realloc(body, cur_len + field_data_len + DETAILS_DESCR_LEN + 8); cur_len += sprintf(body + cur_len, "%s", new_text); for (i = cur_len; i < DETAILS_DESCR_LEN + start_len; i++) { if ((cur_len + start_len) % 2) cur_len += sprintf(body + cur_len, "."); else cur_len += sprintf(body + cur_len, " "); } sprintf(body + cur_len, " : %s\n", field_data); } else { body = realloc(body, cur_len + str_len + field_data_len + 8); sprintf(body + cur_len, "%s : %s\n",new_text, field_data); } } return body; } /** * __body_init_status - * @header: * @num_lines: number of lines * @type: type index * * Returns: * data buffer **/ static char *__body_init_status(char **header, int *num_lines, int type) { char *buffer = NULL; int header_lines = 0; int j; char *x_header; for (j = 0, x_header = _(header[j]); strlen(x_header) != 0; j++, x_header = _(header[j])) buffer = add_string_to_body(buffer, x_header, "", &header_lines); buffer = status_header(buffer, &header_lines, type); if (num_lines != NULL) *num_lines = header_lines; return buffer; } /** * body_init_status - * @buffer: char array * @header: * @num_lines: number of lines * * Returns: * nothing **/ static void body_init_status(char *buffer[2], char **header, int *num_lines) { int z; for (z = 0; z < 2; z++) buffer[z] = __body_init_status(header, num_lines, z); } /** * body_init_status_conc - * @buffer: char array * @header: * @num_lines: number of lines * * Returns: * nothing **/ static void body_init_status_conc(char *buffer[3], char **header, int *num_lines) { int z; for (z = 0; z < 3; z++) buffer[z] = __body_init_status(header, num_lines, (z + 8)); } /** * body_init_status_enclosure - * @buffer: char array * @header: * @num_lines: number of lines * * Returns: * nothing **/ static void body_init_status_enclosure(char *buffer[2], char **header, int *num_lines) { buffer[0] = __body_init_status(header, num_lines, 12); buffer[1] = __body_init_status(header, num_lines, 13); } /** * __body_init - * @buffer: char array * @header: * @num_lines: number of lines * * Returns: * data buffer **/ static char *__body_init(char *buffer, char **header, int *num_lines) { int j; int header_lines = 0; char *x_header; if (num_lines) header_lines = *num_lines; for (j = 0, x_header = _(header[j]); strlen(x_header) != 0; j++, x_header = _(header[j])) { if (j == 0) buffer = add_string_to_body(buffer, x_header, "", &header_lines); else buffer = add_string_to_body(buffer, x_header, " ", &header_lines); } if (num_lines) *num_lines = header_lines; return buffer; } /** * body_init - * @header: * @num_lines: number of lines * * Returns: * data buffer **/ static char *body_init(char **header, int *num_lines) { if (num_lines) *num_lines = 0; return __body_init(NULL, header, num_lines); } /** * __complete_screen_driver_curses - * @n_screen: * @complete: % complete string * @allow_exit: allow exit flag * * Returns: * 0 if success / non-zero on failure **/ static int __complete_screen_driver_curses(s_node *n_screen, char *complete, int allow_exit) { int stdscr_max_y,stdscr_max_x,center,len; int ch; char *exit = _("Return to menu, current operations will continue."); char *exit_str; getmaxyx(stdscr,stdscr_max_y,stdscr_max_x); clear(); center = stdscr_max_x/2; len = strlen(n_screen->title); /* add the title at the center of stdscr */ mvaddstr(0,(center - len/2) > 0 ? center-len/2:0, n_screen->title); mvaddstr(2,0,n_screen->body); len = strlen(complete); mvaddstr(stdscr_max_y/2,(center - len/2) > 0 ? center - len/2:0, complete); if (allow_exit) { exit_str = malloc(strlen(exit) + strlen(EXIT_KEY_LABEL) + 8); sprintf(exit_str, EXIT_KEY_LABEL"%s",exit); mvaddstr(stdscr_max_y - 3, 2, exit_str); nodelay(stdscr, TRUE); do { ch = getch(); if (IS_EXIT_KEY(ch)) { nodelay(stdscr, FALSE); return EXIT_FLAG; } } while (ch != ERR); nodelay(stdscr, FALSE); } move(stdscr_max_y - 1, stdscr_max_x - 1); refresh(); return 0; } /** * __complete_screen_driver - * @n_screen: * @complete: % complete string * @allow_exit: allow_exit flag * * Returns: * 0 **/ static int __complete_screen_driver(s_node *n_screen, char *complete, int allow_exit) { fprintf(stdout, "\r%s", complete); fflush(stdout); return 0; } /** * complete_screen_driver - * @n_screen: * @percent: % complete value * @allow_exit: allow exit flag * * Returns: * 0 if success / non-zero on failure **/ static int complete_screen_driver(s_node *n_screen, int percent, int allow_exit) { char *complete = _("Complete"); char *percent_comp; int rc; percent_comp = malloc(strlen(complete) + 8); sprintf(percent_comp,"%3d%% %s", percent, complete); if (use_curses) rc = __complete_screen_driver_curses(n_screen, percent_comp, allow_exit); else rc = __complete_screen_driver(n_screen, percent_comp, allow_exit); free(percent_comp); return rc; } /** * time_screen_driver - * @n_screen: * @seconds: number of seconds * @allow_exit: allow exit flag * * Returns: * 0 if success / non-zero on failure **/ static int time_screen_driver(s_node *n_screen, unsigned long seconds, int allow_exit) { char *complete = _("Elapsed Time: "); char *comp_str; int rc, hours, minutes; hours = seconds/3600; seconds -= (hours * 3600); minutes = seconds/60; seconds -= (minutes *60); comp_str = malloc(strlen(complete) + 40); sprintf(comp_str,"%s%d:%02d:%02ld", complete, hours, minutes, seconds); if (use_curses) rc = __complete_screen_driver_curses(n_screen, comp_str, allow_exit); else rc = __complete_screen_driver(n_screen, comp_str, allow_exit); free(comp_str); return rc; } /** * evaluate_device - * @dev: ipr dev struct * @ioa: ipr ioa struct * @new_block_size: not used? * * Returns: * nothing **/ static void evaluate_device(struct ipr_dev *dev, struct ipr_ioa *ioa, int new_block_size) { struct scsi_dev_data *scsi_dev_data = dev->scsi_dev_data; struct ipr_res_addr res_addr; struct ipr_dev_record *dev_record; int device_converted; int timeout = 60; u32 res_handle; if (scsi_dev_data) { res_handle = scsi_dev_data->handle; res_addr.bus = scsi_dev_data->channel; res_addr.target = scsi_dev_data->id; res_addr.lun = scsi_dev_data->lun; } else if (ipr_is_af_dasd_device(dev)) { dev_record = dev->dev_rcd; if (dev_record->no_cfgte_dev) return; res_handle = ntohl(dev->resource_handle); res_addr.bus = dev_record->type2.resource_addr.bus; res_addr.target = dev_record->type2.resource_addr.target; res_addr.lun = dev_record->type2.resource_addr.lun; } else return; /* send evaluate device down */ ipr_evaluate_device(&ioa->ioa, res_handle); device_converted = 0; while (!device_converted && timeout) { /* xxx FIXME how to determine evaluate device completed? */ device_converted = 1; sleep(1); timeout--; } } /** * verify_device - * @dev: ipr dev struct * * Returns: * nothing **/ static void verify_device(struct ipr_dev *dev) { int rc; struct ipr_cmd_status cmd_status; struct sense_data_t sense_data; if (ipr_fast) return; /* Send Test Unit Ready to start device if its a volume set */ if (ipr_is_volume_set(dev)) { ipr_test_unit_ready(dev, &sense_data); return; } if (ipr_is_af(dev)) { if ((rc = ipr_query_command_status(dev, &cmd_status))) return; if (cmd_status.num_records != 0 && cmd_status.record->status == IPR_CMD_STATUS_SUCCESSFUL) { if (ipr_get_blk_size(dev) == 512) evaluate_device(dev, dev->ioa, IPR_JBOD_BLOCK_SIZE); else if (dev->ioa->support_4k && ipr_get_blk_size(dev) == IPR_JBOD_4K_BLOCK_SIZE) evaluate_device(dev, dev->ioa, IPR_JBOD_4K_BLOCK_SIZE); } } else if (dev->scsi_dev_data->type == TYPE_DISK) { rc = ipr_test_unit_ready(dev, &sense_data); if (!rc) { if (ipr_get_blk_size(dev) == dev->ioa->af_block_size && dev->ioa->qac_data->num_records != 0) { enable_af(dev); evaluate_device(dev, dev->ioa, dev->ioa->af_block_size); } else if (dev->ioa->support_4k && ipr_get_blk_size(dev) == IPR_AF_4K_BLOCK_SIZE && dev->ioa->qac_data->num_records != 0) { enable_af(dev); evaluate_device(dev, dev->ioa, IPR_AF_4K_BLOCK_SIZE); } } } } /** * processing - displays "Processing" at the bottom of the screen * * Returns: * nothing **/ static void processing() { int max_y, max_x; if (!use_curses) return; getmaxyx(stdscr,max_y,max_x); move(max_y-1,0); printw(_("Processing")); refresh(); } /** * main_menu - Main menu * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int main_menu(i_container *i_con) { int rc; int j; struct ipr_ioa *ioa; struct screen_output *s_out; struct scsi_dev_data *scsi_dev_data; processing(); check_current_config(false); for_each_ioa(ioa) { for (j = 0; j < ioa->num_devices; j++) { scsi_dev_data = ioa->dev[j].scsi_dev_data; if (!scsi_dev_data || scsi_dev_data->type == IPR_TYPE_ADAPTER) continue; verify_device(&ioa->dev[j]); } } for (j = 0; j < n_main_menu.num_opts; j++) { n_main_menu.body = ipr_list_opts(n_main_menu.body, n_main_menu.options[j].key, n_main_menu.options[j].list_str); } n_main_menu.body = ipr_end_list(n_main_menu.body); s_out = screen_driver(&n_main_menu, 0, NULL); free(n_main_menu.body); n_main_menu.body = NULL; rc = s_out->rc; i_con = s_out->i_con; i_con = free_i_con(i_con); free(s_out); return rc; } /** * print_standalone_disks - * @ioa: ipr ioa struct * @i_con: i_container struct * @buffer: * @type: * * Returns: * number of lines printed **/ static int print_standalone_disks(struct ipr_ioa *ioa, i_container **i_con, char **buffer, int type) { struct ipr_dev *dev; int k; int num_lines = 0; __for_each_standalone_disk(ioa, dev) { print_dev (k, dev, buffer, "%1", type+k); *i_con = add_i_con(*i_con, "\0", dev); num_lines++; } return num_lines; } /** * print_hotspare_disks - * @ioa: ipr ioa struct * @i_con: i_container struct * @buffer: * @type: * * Returns: * number of lines printed **/ static int print_hotspare_disks(struct ipr_ioa *ioa, i_container **i_con, char **buffer, int type) { struct ipr_dev *dev; int k; int num_lines = 0; for_each_hot_spare(ioa, dev) { print_dev(k, dev, buffer, "%1", type+k); *i_con = add_i_con(*i_con, "\0", dev); num_lines++; } return num_lines; } /** * print_ses_devices - * @ioa: ipr ioa struct * @i_con: i_container struct * @buffer: * @type: * * Returns: * number of lines printed **/ static int print_ses_devices(struct ipr_ioa *ioa, i_container **i_con, char **buffer, int type) { struct ipr_dev *dev; int k; int num_lines = 0; for_each_ses(ioa, dev) { print_dev(k, dev, buffer, "%1", type+k); *i_con = add_i_con(*i_con, "\0", dev); num_lines++; } return num_lines; } /** * print_sas_ses_devices - * @ioa: ipr ioa struct * @i_con: i_container struct * @buffer: * @type: * * Returns: * number of lines printed **/ static int print_sas_ses_devices(struct ipr_ioa *ioa, i_container **i_con, char **buffer, int type) { struct ipr_dev *dev; int k; int num_lines = 0; if (__ioa_is_spi(ioa)) return 0; for_each_ses(ioa, dev) { print_dev(k, dev, buffer, "%1", type+k); *i_con = add_i_con(*i_con, "\0", dev); num_lines++; } return num_lines; } /** * print_vsets - * @ioa: ipr ioa struct * @i_con: i_container struct * @buffer: * @type: * * Returns: * number of lines printed **/ static int print_vsets(struct ipr_ioa *ioa, i_container **i_con, char **buffer, int type) { int k; int num_lines = 0; struct ipr_dev *vset, *dev; /* print volume set and associated devices*/ for_each_vset(ioa, vset) { print_dev(k, vset, buffer, "%1", type+k); *i_con = add_i_con(*i_con, "\0", vset); num_lines++; for_each_dev_in_vset(vset, dev) { print_dev(k, dev, buffer, "%1", type+k); *i_con = add_i_con(*i_con, "\0", dev); num_lines++; } } return num_lines; } /** * disk_status - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int disk_status(i_container *i_con) { int rc, k; int len = 0; int num_lines = 0; struct ipr_ioa *ioa; struct ipr_dev *dev; struct screen_output *s_out; int header_lines; char *buffer[2]; int toggle = 1; processing(); rc = RC_SUCCESS; i_con = free_i_con(i_con); check_current_config(false); body_init_status(buffer, n_disk_status.header, &header_lines); for_each_ioa(ioa) { if (!ioa->ioa.scsi_dev_data) continue; print_dev(k, &ioa->ioa, buffer, "%1", 2+k); i_con = add_i_con(i_con, "\0", &ioa->ioa); num_lines++; /* xxxx remove? */ __for_each_standalone_disk(ioa, dev) verify_device(dev); num_lines += print_standalone_disks(ioa, &i_con, buffer, 2); num_lines += print_hotspare_disks(ioa, &i_con, buffer, 2); num_lines += print_vsets(ioa, &i_con, buffer, 2); num_lines += print_ses_devices(ioa, &i_con, buffer, 2); } if (num_lines == 0) { for (k = 0; k < 2; k++) { len = strlen(buffer[k]); buffer[k] = realloc(buffer[k], len + strlen(_(no_dev_found)) + 8); sprintf(buffer[k] + len, "\n%s", _(no_dev_found)); } } do { n_disk_status.body = buffer[toggle&1]; s_out = screen_driver(&n_disk_status, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_disk_status.body = NULL; return rc; } /** * device_details_get_device - * @i_con: i_container struct * @device: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ static int device_details_get_device(i_container *i_con, struct ipr_dev **device) { i_container *temp_i_con; int invalid=0; int dev_num = 0; struct ipr_dev *cur_device; char *input; if (!i_con) return 1; for (temp_i_con = i_con_head; temp_i_con && !invalid; temp_i_con = temp_i_con->next_item) { cur_device = (struct ipr_dev *)(temp_i_con->data); if (!cur_device) continue; input = temp_i_con->field_data; if (!input) continue; if (strcmp(input, "1") == 0) { if (dev_num) { dev_num++; break; } else { *device = cur_device; dev_num++; } } else if ((strcmp(input, " ") != 0) && (input != NULL) && (strcmp(input, "") != 0)) { invalid = 1; } } if (invalid) /* The selection is not valid */ return 15; if (dev_num == 0) /* Invalid option. No devices selected. */ return 17; else if (dev_num != 1) /* Error. More than one unit was selected. */ return 16; return 0; } /** * ioa_details - get IOA details * @body: data buffer * @dev: ipr dev struct * * Returns: * body **/ static char *ioa_details(char *body, struct ipr_dev *dev) { struct ipr_ioa_vpd ioa_vpd; struct ipr_cfc_vpd cfc_vpd; struct ipr_cache_cap_vpd cc_vpd; struct ipr_dram_vpd dram_vpd; struct ipr_inquiry_page0 page0_inq; struct scsi_dev_data *scsi_dev_data = dev->scsi_dev_data; struct ipr_dual_ioa_entry *ioa_entry; int rc, i; char buffer[200]; int cache_size, dram_size; u32 fw_level; memset(&ioa_vpd, 0, sizeof(ioa_vpd)); memset(&cfc_vpd, 0, sizeof(cfc_vpd)); memset(&cc_vpd, 0, sizeof(cc_vpd)); memset(&dram_vpd, 0, sizeof(dram_vpd)); ipr_inquiry(dev, IPR_STD_INQUIRY, &ioa_vpd, sizeof(ioa_vpd)); rc = ipr_inquiry(dev, 0, &page0_inq, sizeof(page0_inq)); for (i = 0; !rc && i < page0_inq.page_length; i++) { if (page0_inq.supported_page_codes[i] == 1) { ipr_inquiry(dev, 1, &cfc_vpd, sizeof(cfc_vpd)); break; } if (page0_inq.supported_page_codes[i] == 0xC4) { ipr_inquiry(dev, 0xC4, &cc_vpd, sizeof(cc_vpd)); break; } } ipr_inquiry(dev, 2, &dram_vpd, sizeof(dram_vpd)); fw_level = get_ioa_fw_version(dev->ioa); body = add_line_to_body(body,"", NULL); ipr_strncpy_0(buffer, scsi_dev_data->vendor_id, IPR_VENDOR_ID_LEN); body = add_line_to_body(body,_("Manufacturer"), buffer); ipr_strncpy_0(buffer, scsi_dev_data->product_id, IPR_PROD_ID_LEN); body = add_line_to_body(body,_("Machine Type and Model"), buffer); sprintf(buffer,"%08X", fw_level); body = add_line_to_body(body,_("Firmware Version"), buffer); if (!dev->ioa->ioa_dead) { ipr_strncpy_0(buffer, (char *)ioa_vpd.std_inq_data.serial_num, IPR_SERIAL_NUM_LEN); body = add_line_to_body(body,_("Serial Number"), buffer); ipr_strncpy_0(buffer, (char *)ioa_vpd.ascii_part_num, IPR_VPD_PART_NUM_LEN); body = add_line_to_body(body,_("Part Number"), buffer); ipr_strncpy_0(buffer, (char *)ioa_vpd.ascii_plant_code, IPR_VPD_PLANT_CODE_LEN); body = add_line_to_body(body,_("Plant of Manufacturer"), buffer); if (cfc_vpd.cache_size[0]) { ipr_strncpy_0(buffer, (char *)cfc_vpd.cache_size, IPR_VPD_CACHE_SIZE_LEN); cache_size = strtoul(buffer, NULL, 16); sprintf(buffer,"%d MB", cache_size); body = add_line_to_body(body,_("Cache Size"), buffer); } if (ntohl(cc_vpd.write_cache_size)) { sprintf(buffer,"%d MB", ntohl(cc_vpd.write_cache_size)); body = add_line_to_body(body,_("Write Cache Size"), buffer); } if (ntohl(cc_vpd.data_store_size)) { sprintf(buffer,"%d MB", ntohl(cc_vpd.data_store_size)); body = add_line_to_body(body,_("DRAM Size"), buffer); } else { ipr_strncpy_0(buffer, (char *)dram_vpd.dram_size, IPR_VPD_DRAM_SIZE_LEN); dram_size = strtoul(buffer, NULL, 16); sprintf(buffer, "%d MB", dram_size); body = add_line_to_body(body,_("DRAM Size"), buffer); } } body = add_line_to_body(body,_("Resource Name"), dev->gen_name); body = add_line_to_body(body,"", NULL); body = add_line_to_body(body,_("Physical location"), NULL); body = add_line_to_body(body,_("PCI Address"), dev->ioa->pci_address); if (dev->ioa->sis64) body = add_line_to_body(body,_("Resource Path"), scsi_dev_data->res_path); sprintf(buffer,"%d", dev->ioa->host_num); body = add_line_to_body(body,_("SCSI Host Number"), buffer); if (strlen(dev->ioa->physical_location)) body = add_line_to_body(body,_("Platform Location"), dev->ioa->physical_location); if (dev->ioa->dual_raid_support) { ioa_entry = (struct ipr_dual_ioa_entry *) (((unsigned long)&dev->ioa->ioa_status.cap) + ntohl(dev->ioa->ioa_status.cap.length)); body = add_line_to_body(body,"", NULL); body = add_line_to_body(body,_("Current Dual Adapter State"), dev->ioa->dual_state); body = add_line_to_body(body,_("Preferred Dual Adapter State"), dev->ioa->preferred_dual_state); if (ntohl(dev->ioa->ioa_status.num_entries)) { ipr_strncpy_0(buffer, (char *)ioa_entry->fmt0.remote_vendor_id, IPR_VENDOR_ID_LEN); body = add_line_to_body(body,_("Remote Adapter Manufacturer"), buffer); ipr_strncpy_0(buffer, (char *)ioa_entry->fmt0.remote_prod_id, IPR_PROD_ID_LEN); body = add_line_to_body(body,_("Remote Adapter Machine Type And Model"), buffer); ipr_strncpy_0(buffer, (char *)ioa_entry->fmt0.remote_sn, IPR_SERIAL_NUM_LEN); body = add_line_to_body(body,_("Remote Adapter Serial Number"), buffer); } } if (dev->ioa->asymmetric_access) { body = add_line_to_body(body,"", NULL); if (dev->ioa->asymmetric_access_enabled) body = add_line_to_body(body,_("Current Asymmetric Access State"), _("Enabled")); else body = add_line_to_body(body,_("Current Asymmetric Access State"), _("Disabled")); } if (dev->ioa->has_cache) { if (get_ioa_caching(dev->ioa) == IPR_IOA_REQUESTED_CACHING_DISABLED) body = add_line_to_body(body,_("Current Requested Caching Mode"), _("Disabled")); else body = add_line_to_body(body,_("Current Requested Caching Mode"), _("Default")); } return body; } /** * vset_array_details - get vset/array details * @body: data buffer * @dev: ipr dev struct * * Returns: * body **/ static char *vset_array_details(char *body, struct ipr_dev *dev) { struct ipr_read_cap16 read_cap16; unsigned long long max_user_lba_int; long double device_capacity; double lba_divisor; struct ipr_array_record *array_rcd = dev->array_rcd; char buffer[100]; int rc; int scsi_channel, scsi_id, scsi_lun; if (dev->scsi_dev_data) { scsi_channel = dev->scsi_dev_data->channel; scsi_id = dev->scsi_dev_data->id; scsi_lun = dev->scsi_dev_data->lun; } else if (!dev->ioa->sis64) { scsi_channel = array_rcd->type2.last_resource_addr.bus; scsi_id = array_rcd->type2.last_resource_addr.target; scsi_lun = array_rcd->type2.last_resource_addr.lun; } body = add_line_to_body(body,"", NULL); ipr_strncpy_0(buffer, (char *)dev->vendor_id, IPR_VENDOR_ID_LEN); body = add_line_to_body(body,_("Manufacturer"), buffer); sprintf(buffer,"RAID %s", dev->prot_level_str); body = add_line_to_body(body,_("RAID Level"), buffer); if (ntohs(dev->stripe_size) > 1024) sprintf(buffer,"%d M", ntohs(dev->stripe_size)/1024); else sprintf(buffer,"%d k", ntohs(dev->stripe_size)); body = add_line_to_body(body,_("Stripe Size"), buffer); /* Do a read capacity to determine the capacity */ rc = ipr_read_capacity_16(dev, &read_cap16); if (!rc && (ntohl(read_cap16.max_user_lba_hi) || ntohl(read_cap16.max_user_lba_lo)) && ntohl(read_cap16.block_length)) { max_user_lba_int = ntohl(read_cap16.max_user_lba_hi); max_user_lba_int <<= 32; max_user_lba_int |= ntohl(read_cap16.max_user_lba_lo); device_capacity = max_user_lba_int + 1; lba_divisor = (1000*1000*1000) / ntohl(read_cap16.block_length); sprintf(buffer, "%.2Lf GB", device_capacity / lba_divisor); body = add_line_to_body(body, _("Capacity"), buffer); } if (ipr_is_volume_set(dev)) { body = add_line_to_body(body, _("Resource Name"), dev->dev_name); if (dev->ioa->sis64 && dev->array_rcd->type3.desc[0] != ' ') { ipr_strncpy_0(buffer, (char *)dev->array_rcd->type3.desc, sizeof (dev->array_rcd->type3.desc)); body = add_line_to_body(body,_("Label"), buffer); } } ipr_strncpy_0(buffer, (char *)dev->serial_number, IPR_SERIAL_NUM_LEN); body = add_line_to_body(body,_("Serial Number"), buffer); body = add_line_to_body(body, "", NULL); body = add_line_to_body(body, _("Physical location"), NULL); body = add_line_to_body(body, _("PCI Address"), dev->ioa->pci_address); if (dev->ioa->sis64) body = add_line_to_body(body,_("Resource Path"), dev->scsi_dev_data->res_path); if (dev->scsi_dev_data || !dev->ioa->sis64) { sprintf(buffer, "%d", dev->ioa->host_num); body = add_line_to_body(body, _("SCSI Host Number"), buffer); sprintf(buffer, "%d", scsi_channel); body = add_line_to_body(body, _("SCSI Channel"), buffer); sprintf(buffer, "%d", scsi_id); body = add_line_to_body(body, _("SCSI Id"), buffer); sprintf(buffer, "%d", scsi_lun); body = add_line_to_body(body, _("SCSI Lun"), buffer); } return body; } /** * disk_extended_details - get extended disk details * @body: data buffer * @dev: ipr dev struct * @std_inq: ipr_std_inq_data_long struct * * Returns: * body **/ static char *disk_extended_details(char *body, struct ipr_dev *dev, struct ipr_std_inq_data_long *std_inq) { int rc, i; struct ipr_inquiry_page0 page0_inq; char buffer[100]; char *hex; rc = ipr_inquiry(dev, 0, &page0_inq, sizeof(page0_inq)); for (i = 0; !rc && i < page0_inq.page_length; i++) { if (page0_inq.supported_page_codes[i] == 0xC7) { /* FIXME 0xC7 is SCSD, do further research to find what needs to be tested for this affirmation.*/ /* xxx add_str_to_body that does the strncpy_0? */ body = add_line_to_body(body,"", NULL); body = add_line_to_body(body,_("Extended Details"), NULL); ipr_strncpy_0(buffer, (char *)std_inq->fru_number, IPR_STD_INQ_FRU_NUM_LEN); body = add_line_to_body(body,_("FRU Number"), buffer); ipr_strncpy_0(buffer, (char *)std_inq->ec_level, IPR_STD_INQ_EC_LEVEL_LEN); body = add_line_to_body(body,_("EC Level"), buffer); ipr_strncpy_0(buffer, (char *)std_inq->part_number, IPR_STD_INQ_PART_NUM_LEN); body = add_line_to_body(body,_("Part Number"), buffer); hex = (char *)&std_inq->std_inq_data; sprintf(buffer, "%02X%02X%02X%02X%02X%02X%02X%02X", hex[0], hex[1], hex[2], hex[3], hex[4], hex[5], hex[6], hex[7]); body = add_line_to_body(body,_("Device Specific (Z0)"), buffer); ipr_strncpy_0(buffer, (char *)std_inq->z1_term, IPR_STD_INQ_Z1_TERM_LEN); body = add_line_to_body(body,_("Device Specific (Z1)"), buffer); ipr_strncpy_0(buffer, (char *)std_inq->z2_term, IPR_STD_INQ_Z2_TERM_LEN); body = add_line_to_body(body,_("Device Specific (Z2)"), buffer); ipr_strncpy_0(buffer, (char *)std_inq->z3_term, IPR_STD_INQ_Z3_TERM_LEN); body = add_line_to_body(body,_("Device Specific (Z3)"), buffer); ipr_strncpy_0(buffer, (char *)std_inq->z4_term, IPR_STD_INQ_Z4_TERM_LEN); body = add_line_to_body(body,_("Device Specific (Z4)"), buffer); ipr_strncpy_0(buffer, (char *)std_inq->z5_term, IPR_STD_INQ_Z5_TERM_LEN); body = add_line_to_body(body,_("Device Specific (Z5)"), buffer); ipr_strncpy_0(buffer, (char *)std_inq->z6_term, IPR_STD_INQ_Z6_TERM_LEN); body = add_line_to_body(body,_("Device Specific (Z6)"), buffer); break; } } return body; } /** * disk_details - get disk details * @body: data buffer * @dev: ipr dev struct * * Returns: * body **/ static char *disk_details(char *body, struct ipr_dev *dev) { int rc; struct ipr_std_inq_data_long std_inq; struct ipr_dev_record *device_record; struct ipr_dasd_inquiry_page3 page3_inq; struct ipr_read_cap read_cap; struct ipr_query_res_state res_state; long double device_capacity; double lba_divisor; char product_id[IPR_PROD_ID_LEN+1]; char vendor_id[IPR_VENDOR_ID_LEN+1]; char serial_num[IPR_SERIAL_NUM_LEN+1]; char buffer[100]; int len, scsi_channel, scsi_id, scsi_lun; device_record = (struct ipr_dev_record *)dev->dev_rcd; if (strlen(dev->physical_location) == 0) get_drive_phy_loc(dev->ioa); if (dev->scsi_dev_data) { scsi_channel = dev->scsi_dev_data->channel; scsi_id = dev->scsi_dev_data->id; scsi_lun = dev->scsi_dev_data->lun; } else if (device_record) { scsi_channel = device_record->type2.last_resource_addr.bus; scsi_id = device_record->type2.last_resource_addr.target; scsi_lun = device_record->type2.last_resource_addr.lun; } read_cap.max_user_lba = 0; read_cap.block_length = 0; rc = ipr_inquiry(dev, IPR_STD_INQUIRY, &std_inq, sizeof(std_inq)); if (!rc) { ipr_strncpy_0(vendor_id, (char *)std_inq.std_inq_data.vpids.vendor_id, IPR_VENDOR_ID_LEN); ipr_strncpy_0(product_id, (char *)std_inq.std_inq_data.vpids.product_id, IPR_PROD_ID_LEN); ipr_strncpy_0(serial_num, (char *)std_inq.std_inq_data.serial_num, IPR_SERIAL_NUM_LEN); } else if (device_record) { ipr_strncpy_0(vendor_id, (char *)dev->vendor_id, IPR_VENDOR_ID_LEN); ipr_strncpy_0(product_id, (char *)dev->product_id, IPR_PROD_ID_LEN); ipr_strncpy_0(serial_num, (char *)dev->serial_number, IPR_SERIAL_NUM_LEN); } else { ipr_strncpy_0(vendor_id, dev->scsi_dev_data->vendor_id, IPR_VENDOR_ID_LEN); ipr_strncpy_0(product_id, dev->scsi_dev_data->product_id, IPR_PROD_ID_LEN); serial_num[0] = '\0'; } body = add_line_to_body(body,"", NULL); body = add_line_to_body(body,_("Manufacturer"), vendor_id); body = add_line_to_body(body,_("Product ID"), product_id); rc = ipr_inquiry(dev, 0x03, &page3_inq, sizeof(page3_inq)); if (!rc) { len = sprintf(buffer, "%X%X%X%X", page3_inq.release_level[0], page3_inq.release_level[1], page3_inq.release_level[2], page3_inq.release_level[3]); if (isalnum(page3_inq.release_level[0]) && isalnum(page3_inq.release_level[1]) && isalnum(page3_inq.release_level[2]) && isalnum(page3_inq.release_level[3])) { sprintf(buffer + len, " (%c%c%c%c)", page3_inq.release_level[0], page3_inq.release_level[1], page3_inq.release_level[2], page3_inq.release_level[3]); } body = add_line_to_body(body, _("Firmware Version"), buffer); } if (strcmp(vendor_id,"IBMAS400") == 0) { sprintf(buffer, "%X", std_inq.as400_service_level); body = add_line_to_body(body,_("Level"), buffer); } if (serial_num[0] != '\0') body = add_line_to_body(body, _("Serial Number"), serial_num); memset(&read_cap, 0, sizeof(read_cap)); rc = ipr_read_capacity(dev, &read_cap); if (!rc && ntohl(read_cap.block_length) && ntohl(read_cap.max_user_lba)) { lba_divisor = (1000*1000*1000) / ntohl(read_cap.block_length); device_capacity = ntohl(read_cap.max_user_lba) + 1; sprintf(buffer,"%.2Lf GB", device_capacity / lba_divisor); body = add_line_to_body(body,_("Capacity"), buffer); } if (strlen(dev->dev_name) > 0) body = add_line_to_body(body,_("Resource Name"), dev->dev_name); rc = ipr_query_resource_state(dev, &res_state); if (!rc && !res_state.not_oper && !res_state.not_ready) { if (ioa_is_spi(dev->ioa) && !res_state.prot_dev_failed) { if (ntohl(res_state.gscsi.data_path_width) == 16) body = add_line_to_body(body, _("Wide Enabled"), _("Yes")); else body = add_line_to_body(body, _("Wide Enabled"), _("No")); sprintf(buffer, "%d MB/s", (ntohl(res_state.gscsi.data_xfer_rate) * ntohl(res_state.gscsi.data_path_width))/(10 * 8)); if (res_state.gscsi.data_xfer_rate != 0xffffffff) body = add_line_to_body(body, _("Current Bus Throughput"), buffer); } } body = add_line_to_body(body, "", NULL); body = add_line_to_body(body, _("Physical location"), NULL); body = add_line_to_body(body ,_("PCI Address"), dev->ioa->pci_address); if (dev->ioa->sis64) body = add_line_to_body(body,_("Resource Path"), dev->scsi_dev_data->res_path); sprintf(buffer, "%d", dev->ioa->host_num); body = add_line_to_body(body, _("SCSI Host Number"), buffer); sprintf(buffer, "%d", scsi_channel); body = add_line_to_body(body, _("SCSI Channel"), buffer); sprintf(buffer, "%d", scsi_id); body = add_line_to_body(body, _("SCSI Id"), buffer); sprintf(buffer, "%d", scsi_lun); body = add_line_to_body(body, _("SCSI Lun"), buffer); if (strlen(dev->physical_location)) body = add_line_to_body(body,_("Platform Location"), dev->physical_location); body = disk_extended_details(body, dev, &std_inq); return body; } /** * get_ses_phy_loc - get ses physical location * @dev: ipr dev struct * * Returns: * body **/ int get_ses_phy_loc(struct ipr_dev *dev) { int rc, i, ret = 1; struct ses_inquiry_page0 ses_page0_inq; struct ses_serial_num_vpd ses_vpd_inq; struct esm_serial_num_vpd esm_vpd_inq; char buffer[100]; memset(&ses_vpd_inq, 0, sizeof(ses_vpd_inq)); rc = ipr_inquiry(dev, 0x00, &ses_page0_inq, sizeof(ses_page0_inq)); for (i = 0; !rc && i < ses_page0_inq.page_length; i++) { if (ses_page0_inq.supported_vpd_page[i] == 0x04) { ret = ipr_inquiry(dev, 0x04, &ses_vpd_inq, sizeof(ses_vpd_inq)); break; } } if (ret == 0 ) { dev->physical_location[0] = '\0'; strncat(dev->physical_location, "U", strlen("U")); ipr_strncpy_0(buffer, (char *)ses_vpd_inq.feature_code, sizeof(ses_vpd_inq.feature_code)); strncat(dev->physical_location, buffer, strlen(buffer)); ipr_strncpy_0(buffer, (char *)ses_vpd_inq.count, sizeof(ses_vpd_inq.count)); strncat(dev->physical_location, ".", strlen(".")); strncat(dev->physical_location, buffer, strlen(buffer)); ipr_strncpy_0(buffer, (char *)ses_vpd_inq.ses_serial_num, sizeof(ses_vpd_inq.ses_serial_num)); strncat(dev->physical_location, ".", strlen(".")); strncat(dev->physical_location, buffer, strlen(buffer)); } if (strlen(dev->physical_location)) { for (i = 0; !rc && i < ses_page0_inq.page_length; i++) { if (ses_page0_inq.supported_vpd_page[i] == 0x01) { ret = ipr_inquiry(dev, 0x01, &esm_vpd_inq, sizeof(esm_vpd_inq)); break; } } if (ret == 0 ) { ipr_strncpy_0((char *)&dev->serial_number, (char *)&esm_vpd_inq.esm_serial_num[0], sizeof(esm_vpd_inq.esm_serial_num)); ipr_strncpy_0(buffer, (char *)esm_vpd_inq.frb_label, sizeof(esm_vpd_inq.frb_label)); strncat(dev->physical_location, "-", strlen("-")); strncat(dev->physical_location, buffer, strlen(buffer)); return 0; } } return 1; } int get_drive_phy_loc_with_ses_phy_loc(struct ipr_dev *ses, struct drive_elem_desc_pg *drive_data, int slot_id, char *buf, int conc_maint) { char buffer[DISK_PHY_LOC_LENGTH + 1], *first_hyphen; char unit_phy_loc[PHYSICAL_LOCATION_LENGTH+1]; ipr_strncpy_0(buffer, (char *)drive_data->dev_elem[slot_id].disk_physical_loc, DISK_PHY_LOC_LENGTH); if (strlen(ses->physical_location)) { if (strlen(buffer)) { ipr_strncpy_0(unit_phy_loc, ses->physical_location, PHYSICAL_LOCATION_LENGTH); first_hyphen = strchr(unit_phy_loc, '-'); *first_hyphen = '\0'; sprintf(buf, "%s-%s", unit_phy_loc, buffer); } else sprintf(buf, "%s", "\0"); } else if (strlen(buffer)) { if (strlen(ses->ioa->physical_location)) { ipr_strncpy_0(unit_phy_loc, ses->ioa->physical_location, PHYSICAL_LOCATION_LENGTH); first_hyphen = strchr(unit_phy_loc, '-'); *first_hyphen = '\0'; sprintf(buf, "%s-%s", unit_phy_loc, buffer); } else sprintf(buf, "%s", "\0"); } else sprintf(buf, "%s", "\0"); return 0; } int index_in_page2(struct ipr_encl_status_ctl_pg *ses_data, int slot_id) { int i; for (i = 1; i < IPR_NUM_DRIVE_ELEM_STATUS_ENTRIES; i++) { if (ses_data->elem_status[i].slot_id == slot_id) return (i - 1); } return -1; } /** * ses_details - get ses details * @body: data buffer * @dev: ipr dev struct * * Returns: * body **/ static char *ses_details(char *body, struct ipr_dev *dev) { int rc; u8 release_level[4]; char product_id[IPR_PROD_ID_LEN+1]; char vendor_id[IPR_VENDOR_ID_LEN+1]; char buffer[100]; int len; if (strlen(dev->physical_location) == 0) get_ses_phy_loc(dev); ipr_strncpy_0(vendor_id, dev->scsi_dev_data->vendor_id, IPR_VENDOR_ID_LEN); ipr_strncpy_0(product_id, dev->scsi_dev_data->product_id, IPR_PROD_ID_LEN); ipr_strncpy_0(buffer, (char *)&dev->serial_number, ESM_SERIAL_NUM_LEN); body = add_line_to_body(body,"", NULL); body = add_line_to_body(body,_("Manufacturer"), vendor_id); body = add_line_to_body(body,_("Product ID"), product_id); body = add_line_to_body(body,_("Serial Number"), buffer); rc = ipr_get_fw_version(dev, release_level); if (!rc) { len = sprintf(buffer, "%X%X%X%X", release_level[0], release_level[1], release_level[2], release_level[3]); if (isalnum(release_level[0]) && isalnum(release_level[1]) && isalnum(release_level[2]) && isalnum(release_level[3])) { sprintf(buffer + len, " (%c%c%c%c)", release_level[0], release_level[1], release_level[2], release_level[3]); } body = add_line_to_body(body, _("Firmware Version"), buffer); } if (strlen(dev->gen_name) > 0) body = add_line_to_body(body,_("Resource Name"), dev->gen_name); body = add_line_to_body(body, "", NULL); body = add_line_to_body(body, _("Physical location"), NULL); body = add_line_to_body(body ,_("PCI Address"), dev->ioa->pci_address); if (dev->ioa->sis64) body = add_line_to_body(body,_("Resource Path"), dev->scsi_dev_data->res_path); sprintf(buffer, "%d", dev->ioa->host_num); body = add_line_to_body(body, _("SCSI Host Number"), buffer); sprintf(buffer, "%d", dev->scsi_dev_data->channel); body = add_line_to_body(body, _("SCSI Channel"), buffer); sprintf(buffer, "%d", dev->scsi_dev_data->id); body = add_line_to_body(body, _("SCSI Id"), buffer); sprintf(buffer, "%d", dev->scsi_dev_data->lun); body = add_line_to_body(body, _("SCSI Lun"), buffer); if (strlen(dev->physical_location)) body = add_line_to_body(body,_("Platform Location"), dev->physical_location); return body; } /** * device_details - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int device_details(i_container *i_con) { char *body = NULL; struct ipr_dev *dev; struct screen_output *s_out; s_node *n_screen; int rc; processing(); if ((rc = device_details_get_device(i_con, &dev))) return rc; if (dev->scsi_dev_data && dev->scsi_dev_data->type == IPR_TYPE_ADAPTER) { n_screen = &n_adapter_details; body = ioa_details(body, dev); } else if (ipr_is_volume_set(dev)) { n_screen = &n_vset_details; body = vset_array_details(body, dev); } else if (ipr_is_ses(dev)) { n_screen = &n_ses_details; body = ses_details(body, dev); } else { n_screen = &n_device_details; if (dev->scsi_dev_data) body = disk_details(body, dev); else return rc; } n_screen->body = body; s_out = screen_driver(n_screen, 0, i_con); free(n_screen->body); n_screen->body = NULL; rc = s_out->rc; free(s_out); return rc; } #define IPR_INCLUDE 0 #define IPR_REMOVE 1 #define IPR_ADD_HOT_SPARE 0 #define IPR_RMV_HOT_SPARE 1 #define IPR_FMT_JBOD 0 #define IPR_FMT_RECOVERY 1 /** * hot_spare_screen - Display choices for working with hot spares. * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int hot_spare_screen(i_container *i_con) { int rc; struct screen_output *s_out; int loop; free_raid_cmds(); for (loop = 0; loop < n_hot_spare_screen.num_opts; loop++) { n_hot_spare_screen.body = ipr_list_opts(n_hot_spare_screen.body, n_hot_spare_screen.options[loop].key, n_hot_spare_screen.options[loop].list_str); } n_hot_spare_screen.body = ipr_end_list(n_hot_spare_screen.body); s_out = screen_driver(&n_hot_spare_screen, 0, NULL); free(n_hot_spare_screen.body); n_hot_spare_screen.body = NULL; rc = s_out->rc; free(s_out); return rc; } /** * raid_screen - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int raid_screen(i_container *i_con) { int rc; struct screen_output *s_out; int loop; free_raid_cmds(); for (loop = 0; loop < n_raid_screen.num_opts; loop++) { n_raid_screen.body = ipr_list_opts(n_raid_screen.body, n_raid_screen.options[loop].key, n_raid_screen.options[loop].list_str); } n_raid_screen.body = ipr_end_list(n_raid_screen.body); s_out = screen_driver(&n_raid_screen, 0, NULL); free(n_raid_screen.body); n_raid_screen.body = NULL; rc = s_out->rc; free(s_out); return rc; } /** * raid_status - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int raid_status(i_container *i_con) { int rc, k; int len = 0; int num_lines = 0; struct ipr_ioa *ioa; struct screen_output *s_out; int header_lines; char *buffer[2]; int toggle = 1; processing(); rc = RC_SUCCESS; i_con = free_i_con(i_con); check_current_config(false); body_init_status(buffer, n_raid_status.header, &header_lines); for_each_ioa(ioa) { if (!ioa->ioa.scsi_dev_data) continue; num_lines += print_hotspare_disks(ioa, &i_con, buffer, 2); num_lines += print_vsets(ioa, &i_con, buffer, 2); } if (num_lines == 0) { for (k = 0; k < 2; k++) { len = strlen(buffer[k]); buffer[k] = realloc(buffer[k], len + strlen(_(no_dev_found)) + 8); sprintf(buffer[k] + len, "%s\n", _(no_dev_found)); } } toggle_field = 0; do { n_raid_status.body = buffer[toggle&1]; s_out = screen_driver(&n_raid_status, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_raid_status.body = NULL; return rc; } /** * is_array_in_use - * @ioa: ipr ioa struct * @array_id: array ID * * Returns: * 1 if the array is in use / 0 otherwise * * TODO: This routine no longer works. It was originally designed to work * with the 2.4 kernels. For now, the routine will just return 0. * The rest of the code is being left in place in case this kind of * test becomes possible in the future. * JWB - August 11, 2009 **/ static int is_array_in_use(struct ipr_ioa *ioa, u8 array_id) { int opens; struct ipr_dev *vset; return 0; for_each_vset(ioa, vset) { if (array_id == vset->array_id) { if (!vset->scsi_dev_data) continue; opens = num_device_opens(vset->scsi_dev_data->host, vset->scsi_dev_data->channel, vset->scsi_dev_data->id, vset->scsi_dev_data->lun); if (opens != 0) return 1; } } return 0; } /** * raid_stop - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int raid_stop(i_container *i_con) { int rc, k; int found = 0; char *buffer[2]; struct ipr_ioa *ioa; struct ipr_dev *dev; int header_lines; int toggle = 1; struct screen_output *s_out; processing(); /* empty the linked list that contains field pointers */ i_con = free_i_con(i_con); rc = RC_SUCCESS; check_current_config(false); body_init_status(buffer, n_raid_stop.header, &header_lines); for_each_ioa(ioa) { for_each_vset(ioa, dev) { if (!dev->array_rcd->stop_cand) continue; if ((rc = is_array_in_use(ioa, dev->array_id))) continue; add_raid_cmd_tail(ioa, dev, dev->array_id); i_con = add_i_con(i_con, "\0", raid_cmd_tail); print_dev(k, dev, buffer, "%1", 2+k); found++; } } if (!found) { /* Stop Device Parity Protection Failed */ n_raid_stop_fail.body = body_init(n_raid_stop_fail.header, NULL); s_out = screen_driver(&n_raid_stop_fail, 0, i_con); free(n_raid_stop_fail.body); n_raid_stop_fail.body = NULL; rc = s_out->rc; free(s_out); } else { toggle_field = 0; do { n_raid_stop.body = buffer[toggle&1]; s_out = screen_driver(&n_raid_stop, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); } for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_raid_stop.body = NULL; return rc; } /** * confirm_raid_stop - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int confirm_raid_stop(i_container *i_con) { struct ipr_dev *dev; struct ipr_array_record *array_record; struct array_cmd_data *cur_raid_cmd; struct ipr_ioa *ioa; int rc, k; int found = 0; char *input; char *buffer[2]; i_container *temp_i_con; struct screen_output *s_out; int toggle = 1; int header_lines; found = 0; body_init_status(buffer, n_confirm_raid_stop.header, &header_lines); for_each_icon(temp_i_con) { cur_raid_cmd = (struct array_cmd_data *)(temp_i_con->data); if (!cur_raid_cmd) continue; input = temp_i_con->field_data; array_record = cur_raid_cmd->dev->array_rcd; if (strcmp(input, "1") == 0) { found = 1; cur_raid_cmd->do_cmd = 1; if (!array_record->issue_cmd) array_record->issue_cmd = 1; } else { cur_raid_cmd->do_cmd = 0; if (array_record->issue_cmd) array_record->issue_cmd = 0; } } if (!found) return INVALID_OPTION_STATUS; rc = RC_SUCCESS; for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; ioa = cur_raid_cmd->ioa; print_dev(k, cur_raid_cmd->dev, buffer, "1", 2+k); for_each_dev_in_vset(cur_raid_cmd->dev, dev) print_dev(k, dev, buffer, "1", 2+k); } toggle_field = 0; do { n_confirm_raid_stop.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_raid_stop, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_raid_stop.body = NULL; return rc; } /** * raid_stop_complete - * * Returns: * 21 | EXIT_FLAG if success / 20 | EXIT_FLAG on failure **/ static int raid_stop_complete() { struct ipr_cmd_status cmd_status; struct ipr_cmd_status_record *status_record; int done_bad; int not_done = 0; int rc; struct ipr_ioa *ioa; struct array_cmd_data *cur_raid_cmd; while(1) { done_bad = 0; not_done = 0; for_each_raid_cmd(cur_raid_cmd) { ioa = cur_raid_cmd->ioa; rc = ipr_query_command_status(&ioa->ioa, &cmd_status); if (rc) { done_bad = 1; continue; } for_each_cmd_status(status_record, &cmd_status) { if (status_record->command_code == IPR_STOP_ARRAY_PROTECTION && cur_raid_cmd->array_id == status_record->array_id) { if (status_record->status == IPR_CMD_STATUS_IN_PROGRESS) not_done = 1; else if (status_record->status != IPR_CMD_STATUS_SUCCESSFUL) /* "Stop Parity Protection failed" */ done_bad = 1; break; } } } if (!not_done) { if (done_bad) /* "Stop Parity Protection failed" */ return (20 | EXIT_FLAG); check_current_config(false); /* "Stop Parity Protection completed successfully" */ return (21 | EXIT_FLAG); } not_done = 0; sleep(1); } } /** * do_confirm_raid_stop - * @i_con: i_container struct * * Returns: * 21 | EXIT_FLAG if success / 20 | EXIT_FLAG on failure **/ int do_confirm_raid_stop(i_container *i_con) { struct ipr_dev *vset; struct array_cmd_data *cur_raid_cmd; struct ipr_ioa *ioa; int rc; int max_y, max_x; struct ipr_res_addr *ra; for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; ioa = cur_raid_cmd->ioa; vset = cur_raid_cmd->dev; rc = is_array_in_use(ioa, cur_raid_cmd->array_id); if (rc != 0) { syslog(LOG_ERR, _("Array %s is currently in use and cannot be deleted.\n"), vset->dev_name); return (20 | EXIT_FLAG); } getmaxyx(stdscr, max_y, max_x); move(max_y-1, 0); printw(_("Operation in progress - please wait")); refresh(); if (vset->scsi_dev_data) { ipr_allow_restart(vset, 0); ipr_set_manage_start_stop(vset); ipr_start_stop_stop(vset); ipr_write_dev_attr(vset, "delete", "1"); } if (vset->alt_path && vset->alt_path->scsi_dev_data) { ipr_allow_restart(vset->alt_path, 0); ipr_set_manage_start_stop(vset->alt_path); ipr_start_stop_stop(vset->alt_path); ipr_write_dev_attr(vset->alt_path, "delete", "1"); } ioa->num_raid_cmds++; } sleep(2); for_each_ioa(ioa) { if (ioa->num_raid_cmds == 0) continue; ioa->num_raid_cmds = 0; rc = ipr_stop_array_protection(ioa); if (rc != 0) { if (vset->scsi_dev_data) { ra = &vset->res_addr[0]; ipr_scan(vset->ioa, ra->bus, ra->target, ra->lun); ipr_allow_restart(vset, 1); } if (vset->alt_path && vset->alt_path->scsi_dev_data) { ra = &vset->alt_path->res_addr[0]; ipr_scan(vset->alt_path->ioa, ra->bus, ra->target, ra->lun); ipr_allow_restart(vset->alt_path, 1); } return (20 | EXIT_FLAG); } } rc = raid_stop_complete(); return rc; } /** * raid_start - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int raid_start(i_container *i_con) { int rc, k; int found = 0; char *buffer[2]; struct ipr_ioa *ioa; struct screen_output *s_out; int header_lines; int toggle = 0; u8 array_id; processing(); /* empty the linked list that contains field pointers */ i_con = free_i_con(i_con); rc = RC_SUCCESS; check_current_config(false); body_init_status(buffer, n_raid_start.header, &header_lines); for_each_ioa(ioa) { if (!ioa->start_array_qac_entry) continue; if (ioa->sis64) array_id = ioa->start_array_qac_entry->type3.array_id; else array_id = ioa->start_array_qac_entry->type2.array_id; add_raid_cmd_tail(ioa, &ioa->ioa, array_id); i_con = add_i_con(i_con,"\0",raid_cmd_tail); print_dev(k, &ioa->ioa, buffer, "%1", k); found++; } if (!found) { /* Start Device Parity Protection Failed */ n_raid_start_fail.body = body_init(n_raid_start_fail.header, NULL); s_out = screen_driver(&n_raid_start_fail, 0, i_con); free(n_raid_start_fail.body); n_raid_start_fail.body = NULL; rc = s_out->rc; free(s_out); } else { do { n_raid_start.body = buffer[toggle&1]; s_out = screen_driver(&n_raid_start, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); free_raid_cmds(); } for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_raid_start.body = NULL; return rc; } /** * raid_start_loop - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int raid_start_loop(i_container *i_con) { int rc; struct array_cmd_data *cur_raid_cmd; int found = 0; char *input; i_container *temp_i_con; for_each_icon(temp_i_con) { cur_raid_cmd = (struct array_cmd_data *)(temp_i_con->data); if (!cur_raid_cmd) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { rc = configure_raid_start(temp_i_con); if (RC_SUCCESS == rc) { found = 1; cur_raid_cmd->do_cmd = 1; } else return rc; } else cur_raid_cmd->do_cmd = 0; } if (found != 0) { /* Go to verification screen */ rc = confirm_raid_start(NULL); rc |= EXIT_FLAG; } else rc = INVALID_OPTION_STATUS; /* "Invalid options specified" */ return rc; } /** * configure_raid_start - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int configure_raid_start(i_container *i_con) { char *input; int found; struct ipr_dev *dev; struct array_cmd_data *cur_raid_cmd; int rc, k; char *buffer[2]; struct ipr_ioa *ioa; i_container *i_con2 = NULL; i_container *i_con_head_saved; i_container *temp_i_con = NULL; i_container *temp_i_con2; struct screen_output *s_out; int header_lines; int toggle = 0; rc = RC_SUCCESS; cur_raid_cmd = (struct array_cmd_data *)(i_con->data); ioa = cur_raid_cmd->ioa; body_init_status(buffer, n_configure_raid_start.header, &header_lines); i_con_head_saved = i_con_head; i_con_head = NULL; for_each_af_dasd(ioa, dev) { if (!dev->scsi_dev_data) continue; if (dev->dev_rcd->start_cand && device_supported(dev)) { print_dev(k, dev, buffer, "%1", k); i_con2 = add_i_con(i_con2, "\0", dev); } } do { toggle_field = 0; do { n_configure_raid_start.body = buffer[toggle&1]; s_out = screen_driver(&n_configure_raid_start, header_lines, i_con2); temp_i_con2 = s_out->i_con; rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); if (rc > 0) goto leave; i_con2 = temp_i_con2; found = 0; for_each_icon(temp_i_con) { dev = (struct ipr_dev *)temp_i_con->data; input = temp_i_con->field_data; if (dev && strcmp(input, "1") == 0) { found = 1; dev->dev_rcd->issue_cmd = 1; } } if (found != 0) { /* Go to parameters screen */ rc = configure_raid_parameters(i_con); if ((rc & EXIT_FLAG) || !(rc & CANCEL_FLAG)) goto leave; /* User selected Cancel, clear out current selections for redisplay */ for_each_icon(temp_i_con) { dev = (struct ipr_dev *)temp_i_con->data; input = temp_i_con->field_data; if (dev && (strcmp(input, "1") == 0)) dev->dev_rcd->issue_cmd = 0; } rc = REFRESH_FLAG; continue; } else { s_status.index = INVALID_OPTION_STATUS; rc = REFRESH_FLAG; } } while (rc & REFRESH_FLAG); leave: i_con2 = free_i_con(i_con2); i_con_head = i_con_head_saved; for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_configure_raid_start.body = NULL; return rc; } /** * display_input - * @field_start_row: * @userprt: * * Returns: * 0 if success / non-zero on failure **/ int display_input(int field_start_row, int *userptr) { FIELD *input_fields[3]; FORM *form; WINDOW *box1_win, *field_win; int field_rows = 2; int field_cols = 16; char *input; int ch; int rc = RC_SUCCESS; input_fields[0] = new_field(1, 16, 0, 0, 0, 0); input_fields[1] = new_field(1, 3, 1, 0, 0, 0); input_fields[2] = NULL; set_field_buffer(input_fields[0],0, "Enter new value"); field_opts_off(input_fields[0], O_ACTIVE); field_opts_off(input_fields[0], O_EDIT); box1_win = newwin(field_rows + 2, field_cols + 2, field_start_row - 1, 54); field_win = newwin(field_rows, field_cols, field_start_row, 55); box(box1_win,ACS_VLINE,ACS_HLINE); form = new_form(input_fields); set_current_field(form, input_fields[1]); set_form_win(form,field_win); set_form_sub(form,field_win); post_form(form); form_driver(form, REQ_FIRST_FIELD); wnoutrefresh(box1_win); while (1) { wnoutrefresh(field_win); doupdate(); ch = getch(); if (IS_ENTER_KEY(ch)) { form_driver(form,REQ_VALIDATION); input = field_buffer(input_fields[1],0); sscanf(input, "%d",userptr); break; } else if (IS_EXIT_KEY(ch)) { rc = EXIT_FLAG; break; } else if (IS_CANCEL_KEY(ch)) { rc = CANCEL_FLAG; break; } else if (isascii(ch)) form_driver(form, ch); } wclear(field_win); wrefresh(field_win); delwin(field_win); wclear(box1_win); wrefresh(box1_win); delwin(box1_win); return rc; } /** * configure_raid_parameters - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int configure_raid_parameters(i_container *i_con) { FIELD *input_fields[5]; FIELD *cur_field; FORM *form; ITEM **raid_item = NULL; struct array_cmd_data *cur_raid_cmd = (struct array_cmd_data *)(i_con->data); int raid_index_default; unsigned long raid_index = 0; unsigned long index; char buffer[24]; int rc, i; struct ipr_ioa *ioa; struct ipr_dev *dev; struct ipr_supported_arrays *supported_arrays; struct ipr_array_cap_entry *cap_entry; struct text_str { char line[16]; } *raid_menu_str = NULL, *stripe_menu_str = NULL; char raid_str[16], stripe_str[16], qdepth_str[4]; int ch, start_row; int cur_field_index; int selected_count = 0; int stripe_sz, stripe_sz_mask, stripe_sz_list[16]; struct prot_level *prot_level_list; int *userptr = NULL; int *retptr; int max_x,max_y,start_y,start_x; int qdepth, new_qdepth; getmaxyx(stdscr,max_y,max_x); getbegyx(stdscr,start_y,start_x); rc = RC_SUCCESS; ioa = cur_raid_cmd->ioa; supported_arrays = ioa->supported_arrays; cap_entry = supported_arrays->entry; /* determine number of devices selected for this parity set */ for_each_af_dasd(ioa, dev) { if (dev->dev_rcd->issue_cmd) selected_count++; } if (ioa->sis64) qdepth = selected_count * 16; else qdepth = selected_count * 4; cur_raid_cmd->qdepth = qdepth; /* set up raid lists */ raid_index = 0; raid_index_default = -1; prot_level_list = malloc(sizeof(struct prot_level)); prot_level_list[raid_index].is_valid = 0; for_each_cap_entry(cap_entry, supported_arrays) { if (selected_count <= cap_entry->max_num_array_devices && selected_count >= cap_entry->min_num_array_devices && ((selected_count % cap_entry->min_mult_array_devices) == 0)) { prot_level_list[raid_index].array_cap_entry = cap_entry; prot_level_list[raid_index].is_valid = 1; if (raid_index_default == -1 && !strcmp((char *)cap_entry->prot_level_str, "10")) raid_index_default = raid_index; else if (!strcmp((char *)cap_entry->prot_level_str, IPR_DEFAULT_RAID_LVL)) raid_index_default = raid_index; raid_index++; prot_level_list = realloc(prot_level_list, sizeof(struct prot_level) * (raid_index + 1)); prot_level_list[raid_index].is_valid = 0; } } if (!raid_index) return 69 | EXIT_FLAG; if (raid_index_default == -1) raid_index_default = 0; /* Title */ input_fields[0] = new_field(1, max_x - start_x, /* new field size */ 0, 0, /* upper left corner */ 0, /* number of offscreen rows */ 0); /* number of working buffers */ /* Protection Level */ input_fields[1] = new_field(1, 9, /* new field size */ 8, 44, /* */ 0, /* number of offscreen rows */ 0); /* number of working buffers */ /* Stripe Size */ input_fields[2] = new_field(1, 9, /* new field size */ 9, 44, /* */ 0, /* number of offscreen rows */ 0); /* number of working buffers */ /* Queue depth */ input_fields[3] = new_field(1, 3, /* new field size */ 10, 44, /* */ 0, /* number of offscreen rows */ 0); /* number of working buffers */ input_fields[4] = NULL; raid_index = raid_index_default; cap_entry = prot_level_list[raid_index].array_cap_entry; stripe_sz = ntohs(cap_entry->recommended_stripe_size); form = new_form(input_fields); set_current_field(form, input_fields[1]); set_field_just(input_fields[0], JUSTIFY_CENTER); set_field_just(input_fields[3], JUSTIFY_LEFT); field_opts_off(input_fields[0], O_ACTIVE); field_opts_off(input_fields[1], O_EDIT); field_opts_off(input_fields[2], O_EDIT); field_opts_off(input_fields[3], O_EDIT); set_field_buffer(input_fields[0], /* field to alter */ 0, /* number of buffer to alter */ _("Select Protection Level and Stripe Size")); /* string value to set */ form_opts_off(form, O_BS_OVERLOAD); flush_stdscr(); clear(); set_form_win(form,stdscr); set_form_sub(form,stdscr); post_form(form); mvaddstr(2, 1, _("Default array configurations are shown. To change")); mvaddstr(3, 1, _("setting hit \"c\" for options menu. Highlight desired")); mvaddstr(4, 1, _("option then hit Enter")); mvaddstr(6, 1, _("c=Change Setting")); mvaddstr(8, 0, "Protection Level . . . . . . . . . . . . :"); mvaddstr(9, 0, "Stripe Size . . . . . . . . . . . . . . :"); mvprintw(10, 0, "Queue Depth (default = %3d). . . . . . . :", qdepth); mvaddstr(max_y - 4, 0, _("Press Enter to Continue")); mvaddstr(max_y - 2, 0, EXIT_KEY_LABEL CANCEL_KEY_LABEL); form_driver(form, /* form to pass input to */ REQ_FIRST_FIELD ); /* form request code */ while (1) { sprintf(raid_str,"RAID %s",cap_entry->prot_level_str); set_field_buffer(input_fields[1], /* field to alter */ 0, /* number of buffer to alter */ raid_str); /* string value to set */ if (stripe_sz > 1024) sprintf(stripe_str,"%d M",stripe_sz/1024); else sprintf(stripe_str,"%d k",stripe_sz); set_field_buffer(input_fields[2], /* field to alter */ 0, /* number of buffer to alter */ stripe_str); /* string value to set */ sprintf(qdepth_str,"%3d",qdepth); set_field_buffer(input_fields[3], /* field to alter */ 0, /* number of buffer to alter */ qdepth_str); /* string value to set */ refresh(); ch = getch(); if (IS_EXIT_KEY(ch)) { rc = EXIT_FLAG; goto leave; } else if (IS_CANCEL_KEY(ch)) { rc = CANCEL_FLAG; goto leave; } else if (ch == 'c') { cur_field = current_field(form); cur_field_index = field_index(cur_field); if (cur_field_index == 1) { /* count the number of valid entries */ index = 0; while (prot_level_list[index].is_valid) index++; /* get appropriate memory, the text portion needs to be done up front as the new_item() function uses the passed pointer to display data */ raid_item = realloc(raid_item, sizeof(ITEM **) * (index + 1)); raid_menu_str = realloc(raid_menu_str, sizeof(struct text_str) * (index)); userptr = realloc(userptr, sizeof(int) * (index + 1)); /* set up protection level menu */ index = 0; while (prot_level_list[index].is_valid) { raid_item[index] = (ITEM *)NULL; sprintf(raid_menu_str[index].line,"RAID %s",prot_level_list[index].array_cap_entry->prot_level_str); raid_item[index] = new_item(raid_menu_str[index].line,""); userptr[index] = index; set_item_userptr(raid_item[index], (char *)&userptr[index]); index++; } raid_item[index] = (ITEM *)NULL; start_row = 8; rc = display_menu(raid_item, start_row, index, &retptr); if (rc == RC_SUCCESS) { raid_index = *retptr; /* find recommended stripe size */ cap_entry = prot_level_list[raid_index].array_cap_entry; stripe_sz = ntohs(cap_entry->recommended_stripe_size); } } else if (cur_field_index == 2) { /* count the number of valid entries */ index = 0; for (i = 0; i < 16; i++) { stripe_sz_mask = 1 << i; if ((stripe_sz_mask & ntohs(cap_entry->supported_stripe_sizes)) == stripe_sz_mask) { index++; } } /* get appropriate memory, the text portion needs to be done up front as the new_item() function uses the passed pointer to display data */ raid_item = realloc(raid_item, sizeof(ITEM **) * (index + 1)); stripe_menu_str = realloc(stripe_menu_str, sizeof(struct text_str) * (index)); userptr = realloc(userptr, sizeof(int) * (index + 1)); /* set up stripe size menu */ index = 0; for (i = 0; i < 16; i++) { stripe_sz_mask = 1 << i; raid_item[index] = (ITEM *)NULL; if ((stripe_sz_mask & ntohs(cap_entry->supported_stripe_sizes)) == stripe_sz_mask) { stripe_sz_list[index] = stripe_sz_mask; if (stripe_sz_mask > 1024) sprintf(stripe_menu_str[index].line,"%d M",stripe_sz_mask/1024); else sprintf(stripe_menu_str[index].line,"%d k",stripe_sz_mask); if (stripe_sz_mask == ntohs(cap_entry->recommended_stripe_size)) { sprintf(buffer,_("%s - recommend"),stripe_menu_str[index].line); raid_item[index] = new_item(buffer, ""); } else { raid_item[index] = new_item(stripe_menu_str[index].line, ""); } userptr[index] = index; set_item_userptr(raid_item[index], (char *)&userptr[index]); index++; } } raid_item[index] = (ITEM *)NULL; start_row = 9; rc = display_menu(raid_item, start_row, index, &retptr); if (rc == RC_SUCCESS) stripe_sz = stripe_sz_list[*retptr]; } else if (cur_field_index == 3) { start_row = 10; rc = display_input(start_row, &new_qdepth); if (rc == EXIT_FLAG) goto leave; if (rc == CANCEL_FLAG) continue; if (new_qdepth > 128) qdepth = 128; else qdepth = new_qdepth; continue; } else continue; i = 0; while (raid_item[i] != NULL) free_item(raid_item[i++]); free(raid_item); raid_item = NULL; if (rc == EXIT_FLAG) goto leave; } else if (IS_ENTER_KEY(ch)) { /* set protection level and stripe size appropriately */ cur_raid_cmd->prot_level = cap_entry->prot_level; cur_raid_cmd->stripe_size = stripe_sz; cur_raid_cmd->qdepth = qdepth; goto leave; } else if (ch == KEY_RIGHT) form_driver(form, REQ_NEXT_CHAR); else if (ch == KEY_LEFT) form_driver(form, REQ_PREV_CHAR); else if ((ch == KEY_BACKSPACE) || (ch == 127)) form_driver(form, REQ_DEL_PREV); else if (ch == KEY_DC) form_driver(form, REQ_DEL_CHAR); else if (ch == KEY_IC) form_driver(form, REQ_INS_MODE); else if (ch == KEY_EIC) form_driver(form, REQ_OVL_MODE); else if (ch == KEY_HOME) form_driver(form, REQ_BEG_FIELD); else if (ch == KEY_END) form_driver(form, REQ_END_FIELD); else if (ch == '\t') form_driver(form, REQ_NEXT_FIELD); else if (ch == KEY_UP) form_driver(form, REQ_PREV_FIELD); else if (ch == KEY_DOWN) form_driver(form, REQ_NEXT_FIELD); else if (isascii(ch)) form_driver(form, ch); } leave: unpost_form(form); free_form(form); free_screen(NULL, NULL, input_fields); flush_stdscr(); return rc; } /** * confirm_raid_start - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int confirm_raid_start(i_container *i_con) { struct array_cmd_data *cur_raid_cmd; struct ipr_ioa *ioa; int rc, k; char *buffer[2]; struct ipr_dev *dev; int header_lines; int toggle = 0; struct screen_output *s_out; rc = RC_SUCCESS; body_init_status(buffer, n_confirm_raid_start.header, &header_lines); for_each_raid_cmd(cur_raid_cmd) { if (cur_raid_cmd->do_cmd) { ioa = cur_raid_cmd->ioa; print_dev(k, cur_raid_cmd->dev, buffer, "1", k); for_each_af_dasd(ioa, dev) { if (dev->dev_rcd->issue_cmd) print_dev(k, dev, buffer, "1", k); } } } toggle_field = 0; do { n_confirm_raid_start.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_raid_start, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_raid_start.body = NULL; if (rc > 0) return rc; /* verify the devices are not in use */ for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; ioa = cur_raid_cmd->ioa; rc = is_array_in_use(ioa, cur_raid_cmd->array_id); if (rc != 0) { /* "Start Parity Protection failed." */ rc = RC_19_Create_Fail; syslog(LOG_ERR, _("Devices may have changed state. Command cancelled," " please issue commands again if RAID still desired %s.\n"), ioa->ioa.gen_name); return rc; } } /* now issue the start array command */ for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; ioa = cur_raid_cmd->ioa; rc = ipr_start_array_protection(ioa, cur_raid_cmd->stripe_size, cur_raid_cmd->prot_level); if (rc) return RC_19_Create_Fail; } rc = raid_start_complete(); return rc; } /** * raid_start_complete - * * Returns: * 0 if success / non-zero on failure **/ int raid_start_complete() { struct ipr_cmd_status cmd_status; struct ipr_cmd_status_record *status_record; int done_bad; int not_done = 0; int retry = 1; int timeout; int rc; u32 percent_cmplt = 0; int device_available = 0; int wait_device = 0; int exit_now = 0; struct ipr_ioa *ioa; struct array_cmd_data *cur_raid_cmd; struct ipr_dev *dev; struct ipr_disk_attr attr; while (1) { rc = complete_screen_driver(&n_raid_start_complete, percent_cmplt, 1); if ((rc & EXIT_FLAG) || exit_now) { exit_now = 1; if (device_available) return EXIT_FLAG; } percent_cmplt = 100; done_bad = 0; not_done = 0; for_each_raid_cmd(cur_raid_cmd) { if (cur_raid_cmd->do_cmd == 0) continue; ioa = cur_raid_cmd->ioa; rc = ipr_query_command_status(&ioa->ioa, &cmd_status); if (rc) { done_bad = 1; continue; } for_each_cmd_status(status_record, &cmd_status) { if ((status_record->command_code != IPR_START_ARRAY_PROTECTION) || (cur_raid_cmd->array_id != status_record->array_id)) continue; if (!device_available && (status_record->resource_handle != IPR_IOA_RESOURCE_HANDLE)) { timeout = 30; while (retry && timeout) { check_current_config(false); dev = get_dev_from_handle(ioa, status_record->resource_handle); if (dev && dev->scsi_dev_data) { device_available = 1; if (ipr_init_new_dev(dev)) { sleep(1); timeout--; continue; } else retry = 0; if (ipr_get_dev_attr(dev, &attr)) { syslog(LOG_ERR, _("Unable to read queue_depth")); } else { attr.queue_depth = cur_raid_cmd->qdepth; if (ipr_set_dev_attr(dev, &attr, 1)) syslog(LOG_ERR, _("Unable to set queue_depth")); } } else break; } } if (status_record->status == IPR_CMD_STATUS_IN_PROGRESS) { if (status_record->percent_complete < percent_cmplt) percent_cmplt = status_record->percent_complete; not_done = 1; } else if (status_record->status != IPR_CMD_STATUS_SUCCESSFUL) { done_bad = 1; if (status_record->status == IPR_CMD_STATUS_MIXED_BLK_DEV_CLASESS) { syslog(LOG_ERR, _("Start parity protect to %s failed. " "SSDs and HDDs can not be mixed in an array.\n"), ioa->ioa.gen_name); rc = RC_22_Mixed_Block_Dev_Classes; } else if (status_record->status == IPR_CMD_STATUS_MIXED_LOG_BLK_SIZE) { syslog(LOG_ERR, _("Start parity protect to %s failed. " "non 4K disks and 4K disks can not be mixed in an array.\n"), ioa->ioa.gen_name); rc = RC_91_Mixed_Logical_Blk_Size; } else { syslog(LOG_ERR, _("Start parity protect to %s failed. " "Check device configuration for proper format.\n"), ioa->ioa.gen_name); rc = RC_FAILED; } } else if (!(device_available)) { wait_device++; /* if device did not show up after parity set created, allow exit */ if (wait_device == 20) syslog(LOG_ERR, _("Unable to set queue_depth")); else not_done = 1; } break; } } if (!not_done) { flush_stdscr(); if (done_bad) { if (status_record->status == IPR_CMD_STATUS_MIXED_BLK_DEV_CLASESS) return RC_22_Mixed_Block_Dev_Classes; /* Start Parity Protection failed. */ return RC_19_Create_Fail; } format_done = 1; check_current_config(false); /* Start Parity Protection completed successfully */ return RC_18_Array_Created; } not_done = 0; sleep(1); } } /** * raid_rebuild - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int raid_rebuild(i_container *i_con) { int rc, k; int found = 0; struct ipr_dev *dev; char *buffer[2]; struct ipr_ioa *ioa; struct screen_output *s_out; int header_lines; int toggle=1; processing(); i_con = free_i_con(i_con); rc = RC_SUCCESS; check_current_config(true); body_init_status(buffer, n_raid_rebuild.header, &header_lines); for_each_ioa(ioa) { ioa->num_raid_cmds = 0; for_each_af_dasd(ioa, dev) { if (!dev->dev_rcd->rebuild_cand) continue; add_raid_cmd_tail(ioa, dev, 0); i_con = add_i_con(i_con,"\0",raid_cmd_tail); print_dev(k, dev, buffer, "%1", k); enable_af(dev); found++; ioa->num_raid_cmds++; } } if (!found) { /* Start Device Parity Protection Failed */ n_raid_rebuild_fail.body = body_init(n_raid_rebuild_fail.header, NULL); s_out = screen_driver(&n_raid_rebuild_fail, 0, NULL); free(n_raid_rebuild_fail.body); n_raid_rebuild_fail.body = NULL; rc = s_out->rc; free(s_out); } else { do { n_raid_rebuild.body = buffer[toggle&1]; s_out = screen_driver(&n_raid_rebuild, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); free_raid_cmds(); } for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_raid_rebuild.body = NULL; return rc; } /** * confirm_raid_rebuild - * @i_con: i_container struct * * Returns: * **/ int confirm_raid_rebuild(i_container *i_con) { struct array_cmd_data *cur_raid_cmd; struct ipr_ioa *ioa; int rc; char *buffer[2]; int found = 0; char *input; int k; int header_lines; int toggle=1; i_container *temp_i_con; struct screen_output *s_out; for_each_icon(temp_i_con) { cur_raid_cmd = (struct array_cmd_data *)temp_i_con->data; if (!cur_raid_cmd) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { cur_raid_cmd->do_cmd = 1; if (cur_raid_cmd->dev->dev_rcd) cur_raid_cmd->dev->dev_rcd->issue_cmd = 1; found++; } else { cur_raid_cmd->do_cmd = 0; if (cur_raid_cmd->dev->dev_rcd) cur_raid_cmd->dev->dev_rcd->issue_cmd = 0; } } if (!found) return INVALID_OPTION_STATUS; body_init_status(buffer, n_confirm_raid_rebuild.header, &header_lines); for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; ioa = cur_raid_cmd->ioa; print_dev(k, cur_raid_cmd->dev, buffer, "1", k); } do { n_confirm_raid_rebuild.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_raid_rebuild, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_raid_rebuild.body = NULL; if (rc) return rc; for_each_ioa(ioa) { if (ioa->num_raid_cmds == 0) continue; if ((rc = ipr_rebuild_device_data(ioa))) /* Rebuild failed */ return (29 | EXIT_FLAG); } /* Rebuild started, view Parity Status Window for rebuild progress */ return (28 | EXIT_FLAG); } /** * raid_resync_complete - * * Returns: * EXIT_FLAG / 74 | EXIT_FLAG on success / 73 | EXIT_FLAG otherwise **/ static int raid_resync_complete() { struct ipr_cmd_status cmd_status; struct ipr_cmd_status_record *status_record; int done_bad; int not_done = 0; int rc; u32 percent_cmplt = 0; struct ipr_ioa *ioa; struct array_cmd_data *cur_raid_cmd; while (1) { rc = complete_screen_driver(&n_raid_resync_complete, percent_cmplt, 1); if (rc & EXIT_FLAG) return EXIT_FLAG; percent_cmplt = 100; done_bad = 0; not_done = 0; for_each_raid_cmd(cur_raid_cmd) { if (cur_raid_cmd->do_cmd == 0) continue; ioa = cur_raid_cmd->ioa; rc = ipr_query_command_status(&ioa->ioa, &cmd_status); if (rc) { done_bad = 1; continue; } for_each_cmd_status(status_record, &cmd_status) { if ((status_record->command_code != IPR_RESYNC_ARRAY_PROTECTION) || (cur_raid_cmd->array_id != status_record->array_id)) continue; if (status_record->status == IPR_CMD_STATUS_IN_PROGRESS) { if (status_record->percent_complete < percent_cmplt) percent_cmplt = status_record->percent_complete; not_done = 1; } else if (status_record->status != IPR_CMD_STATUS_SUCCESSFUL) { done_bad = 1; syslog(LOG_ERR, _("Resync array to %s failed.\n"), ioa->ioa.gen_name); rc = RC_FAILED; } break; } } if (!not_done) { flush_stdscr(); if (done_bad) /* Resync array failed. */ return (73 | EXIT_FLAG); /* Resync completed successfully */ return (74 | EXIT_FLAG); } not_done = 0; sleep(1); } } /** * raid_resync - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int raid_resync(i_container *i_con) { int rc, k; int found = 0; struct ipr_dev *dev, *array; char *buffer[2]; struct ipr_ioa *ioa; struct screen_output *s_out; int header_lines; int toggle=1; processing(); i_con = free_i_con(i_con); rc = RC_SUCCESS; check_current_config(true); body_init_status(buffer, n_raid_resync.header, &header_lines); for_each_ioa(ioa) { ioa->num_raid_cmds = 0; for_each_array(ioa, array) { if (!array->array_rcd->resync_cand) continue; dev = array; add_raid_cmd_tail(ioa, dev, dev->array_id); i_con = add_i_con(i_con, "\0", raid_cmd_tail); print_dev(k, dev, buffer, "%1", 2+k); found++; ioa->num_raid_cmds++; } } if (!found) { /* Start Device Parity Protection Failed */ n_raid_resync_fail.body = body_init(n_raid_resync_fail.header, NULL); s_out = screen_driver(&n_raid_resync_fail, 0, NULL); free(n_raid_resync_fail.body); n_raid_resync_fail.body = NULL; rc = s_out->rc; free(s_out); } else { do { n_raid_resync.body = buffer[toggle&1]; s_out = screen_driver(&n_raid_resync, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); free_raid_cmds(); } for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_raid_resync.body = NULL; return rc; } /** * confirm_raid_resync - * @i_con: i_container struct * * Returns: * EXIT_FLAG / 74 | EXIT_FLAG on success / 73 | EXIT_FLAG otherwise * INVALID_OPTION_STATUS if nothing to process **/ int confirm_raid_resync(i_container *i_con) { struct array_cmd_data *cur_raid_cmd; struct ipr_ioa *ioa; int rc; char *buffer[2]; int found = 0; char *input; int k; int header_lines; int toggle=1; i_container *temp_i_con; struct screen_output *s_out; for_each_icon(temp_i_con) { cur_raid_cmd = (struct array_cmd_data *)temp_i_con->data; if (!cur_raid_cmd) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { cur_raid_cmd->do_cmd = 1; if (cur_raid_cmd->dev->dev_rcd) cur_raid_cmd->dev->dev_rcd->issue_cmd = 1; found++; } else { cur_raid_cmd->do_cmd = 0; if (cur_raid_cmd->dev->dev_rcd) cur_raid_cmd->dev->dev_rcd->issue_cmd = 0; } } if (!found) return INVALID_OPTION_STATUS; body_init_status(buffer, n_confirm_raid_resync.header, &header_lines); for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; ioa = cur_raid_cmd->ioa; print_dev(k, cur_raid_cmd->dev, buffer, "1", k); } do { n_confirm_raid_resync.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_raid_resync, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_raid_resync.body = NULL; if (rc) return rc; for_each_ioa(ioa) { if (ioa->num_raid_cmds == 0) continue; if ((rc = ipr_resync_array(ioa))) return (73 | EXIT_FLAG); } return raid_resync_complete(); } /** * do_raid_migrate - Process the command line Migrate Array command. Check the * arguments against the qac data to make sure that the * migration is possible. Then issue the migrate command. * @ioa: struct ipr_ioa * @qac_data: query array config data * @vset: struct ipr_dev * @raid_level: new raid level * @stripe_size: new stripe size * @num_devs: number of devices to add * @fd: file descriptor * * Returns: * 0 if success / non-zero on failure **/ int do_raid_migrate(struct ipr_ioa *ioa, struct ipr_array_query_data *qac_data, struct ipr_dev *vset, char *raid_level, int stripe_size, int num_devs, int fd) { int found = 0, rc = 0; int enc_raid_level; struct ipr_dev *dev = NULL; struct sysfs_dev *sdev; struct ipr_array_cap_entry *cap; struct ipr_array_record *array_rcd; struct ipr_dev_record *dev_rcd; struct ipr_supported_arrays *supported_arrays = NULL; /* check that the given raid level is valid */ for_each_supported_arrays_rcd(supported_arrays, qac_data) { cap = get_cap_entry(supported_arrays, raid_level); if (cap) break; } if (!cap) { fprintf(stderr, "RAID level %s is unsupported.\n", raid_level); return -EINVAL; } enc_raid_level = cap->prot_level; if (ioa->sis64) vset = get_array_from_vset(ioa, vset); /* loop through returned records - looking at array records */ for_each_array_rcd(array_rcd, qac_data) { if (vset->resource_handle == ipr_get_arr_res_handle(ioa, array_rcd)) { if (array_rcd->migrate_cand) { array_rcd->issue_cmd = 1; found = 1; } break; } } /* device is valid, but array migration is not supported */ if (!found) { fprintf(stderr, "Migrate array protection is not currently "); fprintf(stderr, "possible on the %s device.\n", vset->gen_name); return -EINVAL; } /* process stripe size */ if (stripe_size) { /* is stripe size supported? */ if ((stripe_size & ntohs(cap->supported_stripe_sizes)) != stripe_size) { fprintf(stderr, "Unsupported stripe size for "); fprintf(stderr, "selected adapter. %d\n", stripe_size); return -EINVAL; } } else /* get current stripe size */ if (ioa->sis64) stripe_size = array_rcd->type3.stripe_size; else stripe_size = array_rcd->type2.stripe_size; /* if adding devices, check that there are a valid number of them */ if (cap->format_overlay_type == IPR_FORMAT_ADD_DEVICES) { rc = raid_create_check_num_devs(cap, num_devs, 0); if (rc != 0) { fprintf(stderr,"Incorrect number of devices given.\n"); fprintf(stderr,"Minimum number of devices = %d\n", cap->min_num_array_devices); fprintf(stderr,"Maximum number of devices = %d\n", cap->max_num_array_devices); return rc; } } /* if adding devs, verify them and set issue_cmd = 1 */ for_each_dev_rcd(dev_rcd, qac_data) { if (num_devs == 0) break; /* check the given devices for migrate candidate status */ for (sdev = head_sdev; sdev; sdev = sdev->next) { dev = ipr_sysfs_dev_to_dev(sdev); if (ipr_get_dev_res_handle(ioa, dev_rcd) == dev->resource_handle) { if (dev_rcd->migrate_array_prot_cand) dev_rcd->issue_cmd = 1; else { fprintf(stderr, "%s is not available " "for migrate array " "protection\n", dev->gen_name); return -EINVAL; } } } } /* send command to adapter */ rc = ipr_migrate_array_protection(ioa, qac_data, fd, stripe_size, enc_raid_level); return rc; } /** * raid_migrate_cmd - Command line handler for Migrate Array command. Process * the command arguments and prepare for do_raid_migrate(). * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int raid_migrate_cmd(char **args, int num_args) { int i, fd, rc = 0; int next_raid_level = 0, raid_level_set = 0; int next_stripe_size = 0; int stripe_size = 0; int num_devs = 0; char *raid_level = IPR_DEFAULT_RAID_LVL; struct ipr_dev *dev = NULL, *vset = NULL; struct ipr_ioa *ioa = NULL; struct ipr_array_query_data qac_data; memset(&qac_data, 0, sizeof(qac_data)); for (i = 0; i < num_args; i++) { if (strcmp(args[i], "-r") == 0) next_raid_level = 1; else if (strcmp(args[i], "-s") == 0) next_stripe_size = 1; else if (next_raid_level) { next_raid_level = 0; raid_level_set = 1; raid_level = args[i]; } else if (next_stripe_size) { next_stripe_size = 0; stripe_size = strtoul(args[i], NULL, 10); } else { dev = find_dev(args[i]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[i]); return -EINVAL; } if (ipr_is_volume_set(dev)) { if (vset) { fprintf(stderr, "Invalid parameters. " "Only one disk " "array can be specified.\n"); return -EINVAL; } vset = dev; ioa = vset->ioa; continue; } if (!ipr_is_af_dasd_device(dev)) { fprintf(stderr, "Invalid device specified. " "Device must be formatted " "to Advanced Function Format.\n"); return -EINVAL; } else { /* keep track of these */ num_devs++; ipr_add_sysfs_dev(dev, &head_sdev, &tail_sdev); } } } if (vset == NULL) { fprintf(stderr, "No RAID device specified\n"); return -EINVAL; } if (!raid_level_set) { fprintf(stderr, "No RAID level specified\n"); return -EINVAL; } /* lock the ioa device */ fd = open_and_lock(ioa->ioa.gen_name); if (fd <= 1) return -EIO; /* query array configuration for volume set migrate status */ rc = __ipr_query_array_config(ioa, fd, 0, 0, 1, vset->array_id, &qac_data); if (rc != 0) { fprintf(stderr, "Array query failed. rc = %d\n", rc); close(fd); return -EINVAL; } /* Check arguments against the qac data and issue the migrate command */ rc = do_raid_migrate(ioa, &qac_data, vset, raid_level, stripe_size, num_devs, fd); close(fd); return rc; } /** * raid_migrate_complete - Display a status screen showing the command * completion percentage. * * Returns: * 0 if success / non-zero on failure **/ int raid_migrate_complete() { struct ipr_cmd_status cmd_status; struct ipr_cmd_status_record *status_record; int done_bad; int not_done = 0; int rc; u32 percent_cmplt = 0; int device_available = 0; int exit_now = 0; struct ipr_ioa *ioa; struct array_cmd_data *cur_raid_cmd; struct ipr_dev *dev; ENTER; while (1) { rc = complete_screen_driver(&n_raid_migrate_complete, percent_cmplt, 1); if (rc & EXIT_FLAG || exit_now) { exit_now = 1; if (device_available) return EXIT_FLAG; } percent_cmplt = 100; done_bad = 0; not_done = 0; for_each_raid_cmd(cur_raid_cmd) { if (cur_raid_cmd->do_cmd == 0) continue; ioa = cur_raid_cmd->ioa; rc = ipr_query_command_status(&ioa->ioa, &cmd_status); if (rc) { done_bad = 1; continue; } for_each_cmd_status(status_record, &cmd_status) { if ((status_record->command_code != IPR_MIGRATE_ARRAY_PROTECTION) || (cur_raid_cmd->array_id != status_record->array_id)) continue; if (!device_available && (status_record->resource_handle != IPR_IOA_RESOURCE_HANDLE)) { check_current_config(false); dev = get_dev_from_handle(ioa, status_record->resource_handle); if (dev && dev->scsi_dev_data) device_available = 1; } if (status_record->status == IPR_CMD_STATUS_IN_PROGRESS) { if (status_record->percent_complete < percent_cmplt) percent_cmplt = status_record->percent_complete; not_done = 1; } else if (status_record->status != IPR_CMD_STATUS_SUCCESSFUL) { done_bad = 1; ioa_err(ioa, "Migrate array protection failed.\n"); rc = RC_FAILED; } else if (!device_available) not_done = 1; break; } } if (!not_done) { flush_stdscr(); if (done_bad) /* Migrate Array Protection failed. */ return RC_80_Migrate_Prot_Fail; check_current_config(false); /* Migrate Array Protection completed successfully */ LEAVE; return RC_79_Migrate_Prot_Success; } not_done = 0; sleep(1); } } /** * confirm_raid_migrate - Confirm that the migration choices are correct. * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int confirm_raid_migrate() { struct array_cmd_data *cur_raid_cmd; int rc, k; char *buffer[2]; struct screen_output *s_out; int toggle = 1; int header_lines; ENTER; body_init_status(buffer, n_confirm_raid_migrate.header, &header_lines); /* Display the array being migrated. Also display any disks if they are required for the migration. */ for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; print_dev(k, cur_raid_cmd->dev, buffer, "1", 2+k); } rc = RC_SUCCESS; toggle_field = 0; do { n_confirm_raid_migrate.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_raid_migrate, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_raid_migrate.body = NULL; LEAVE; return rc; } /** * choose_migrate_disks - Choose the additional disks required for migration. * @ioa: * @qac: i_query_array_data struct * @cap_entry: * * Returns: * 0 if success / non-zero on failure **/ int choose_migrate_disks(struct ipr_ioa *ioa, struct ipr_array_query_data *qac, struct ipr_array_cap_entry *cap_entry) { struct array_cmd_data *tmp_raid_cmd; struct ipr_dev_record *dev_rcd; struct ipr_dev *dev; i_container *dev_i_con, *temp_i_con; int rc, min, max, mult; int k = 0; char *buffer[2]; char info[256]; int header_lines = 0; struct screen_output *s_out; int toggle = 0; int found = 0; char *input; ENTER; min = cap_entry->min_num_array_devices; max = cap_entry->max_num_array_devices; mult = cap_entry->min_mult_array_devices; sprintf(info, _("A minimum of %d disks must be selected.\n"), min); buffer[0] = add_string_to_body(NULL, info, "", &header_lines); buffer[1] = add_string_to_body(NULL, info, "", &header_lines); sprintf(info, _("A maximum of %d disks must be selected.\n"), max); buffer[0] = add_string_to_body(buffer[0], info, "", &header_lines); buffer[1] = add_string_to_body(buffer[1], info, "", &header_lines); sprintf(info, _("The number of disks selected must be a multiple of %d.\n\n"), mult); buffer[0] = add_string_to_body(buffer[0], info, "", &header_lines); buffer[1] = add_string_to_body(buffer[1], info, "", &header_lines); buffer[0] = status_header(buffer[0], &header_lines, 0); buffer[1] = status_header(buffer[1], &header_lines, 1); /* Ensure i_con_head gets set to the head of the disk candidate list in add_i_con() */ dev_i_con = NULL; for_each_dev_rcd(dev_rcd, qac) { if (!dev_rcd->migrate_array_prot_cand) continue; dev = get_dev_from_handle(ioa, ipr_get_dev_res_handle(ioa, dev_rcd)); if (dev) { print_dev(k, dev, buffer, "%1", 2); add_raid_cmd_tail(NULL, dev, 0); /* Add disk candidates to the i_con list. */ dev_i_con = add_i_con(dev_i_con, "\0", raid_cmd_tail); } } do { toggle_field = 0; do { n_raid_migrate_add_disks.body = buffer[toggle&1]; /* select disks to use */ s_out = screen_driver(&n_raid_migrate_add_disks, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); if (rc) break; found = 0; /* Loop through, count the selected devices and * set the do_cmd flag. */ for_each_icon(temp_i_con) { tmp_raid_cmd = temp_i_con->data; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { found++; tmp_raid_cmd->do_cmd = 1; } } if (found) { rc = raid_create_check_num_devs(cap_entry, found, 1); if (rc == 0) break; if (rc == RC_77_Too_Many_Disks) { s_status.num = cap_entry->max_num_array_devices; s_status.index = RC_77_Too_Many_Disks; } if (rc == RC_78_Too_Few_Disks) { s_status.num = cap_entry->min_num_array_devices; s_status.index = RC_78_Too_Few_Disks; } if (rc == RC_25_Devices_Multiple) { s_status.num = cap_entry->min_mult_array_devices; s_status.index = RC_25_Devices_Multiple; } /* clear the do_cmd flag */ for_each_icon(temp_i_con) { tmp_raid_cmd = (struct array_cmd_data *)temp_i_con->data; input = temp_i_con->field_data; if (tmp_raid_cmd && strcmp(input, "1") == 0) tmp_raid_cmd->do_cmd = 0; } } rc = REFRESH_FLAG; } while (rc & REFRESH_FLAG); LEAVE; return rc; } /** * do_ipr_migrate_array_protection - Migrate array protection for an array * @array_i_con: i_container for selected array * @ioa: ipr ioa struct * @qac_data: struct ipr_array_query_data * @stripe_size: new or existing stripe size for array * @prot_level: new protection (RAID) level for array * @array_id: array id * * Returns: * 0 if success / non-zero on failure **/ int do_ipr_migrate_array_protection(i_container *array_i_con, struct ipr_ioa *ioa, struct ipr_array_query_data *qac, int stripe_size, int prot_level, u8 array_id) { int fd, rc; i_container *temp_i_con; struct array_cmd_data *acd; struct ipr_array_record *array_rcd; struct ipr_dev_record *dev_rcd; struct ipr_array_query_data new_qac; char *input; ENTER; /* Now that we have the selected array, lock the ioa * device, then issue another query to make sure that * the adapter's version of the QAC data matches what * we are processing. */ fd = open_and_lock(ioa->ioa.gen_name); if (fd <= 1) return fd; rc = __ipr_query_array_config(ioa, fd, 0, 0, 1, array_id, &new_qac); if (rc) { close(fd); return rc; } /* Set the "do it" bit for the select disks if needed. */ for_each_icon(temp_i_con) { acd = temp_i_con->data; input = temp_i_con->field_data; if (acd && strcmp(input, "1") == 0) for_each_dev_rcd(dev_rcd, &new_qac) if (acd->dev->resource_handle == ipr_get_dev_res_handle(ioa, dev_rcd)) dev_rcd->issue_cmd = 1; } /* get the array command data and associated data from the data field of the passed in array info container. */ acd = array_i_con->data; /* Set the "do it" bit for the selected array. */ for_each_array_rcd(array_rcd, &new_qac) if (acd->dev->resource_handle == ipr_get_arr_res_handle(ioa, array_rcd)) array_rcd->issue_cmd = 1; rc = ipr_migrate_array_protection(ioa, &new_qac, fd, stripe_size, prot_level); close(fd); LEAVE; return rc; } /** * configure_raid_migrate - The array to migrate has been selected, now get the * new RAID level. * @array_i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int configure_raid_migrate(i_container *array_i_con) { FIELD *input_fields[3]; FIELD *cur_field; FORM *form; ITEM **raid_item = NULL; struct array_cmd_data *cur_raid_cmd; int raid_index_default = 0; unsigned long raid_index = 0; unsigned long index; int rc, i; struct ipr_ioa *ioa; struct ipr_dev *dev; struct ipr_array_query_data qac; struct ipr_supported_arrays *supported_arrays; struct ipr_array_cap_entry *cap_entry; struct text_str { char line[16]; } *raid_menu_str = NULL; char raid_str[16]; char dev_str[60]; int ch, start_row; int cur_field_index; struct prot_level *prot_level_list; int *userptr = NULL; int *retptr; int max_x,max_y,start_y,start_x; i_container *saved_i_con_head; ENTER; getmaxyx(stdscr,max_y,max_x); getbegyx(stdscr,start_y,start_x); /* get the array command data and associated data from the data field of the passed in array info container. */ cur_raid_cmd = array_i_con->data; ioa = cur_raid_cmd->ioa; dev = cur_raid_cmd->dev; /* Get the QAC data for the chosen array. */ rc = ipr_query_array_config(ioa, 0, 0, 1, cur_raid_cmd->array_id, &qac); if (rc) return RC_80_Migrate_Prot_Fail | EXIT_FLAG; /* allows cancelling out if the user doesn't change the existing protection level. */ rc = CANCEL_FLAG; /* Save the current i_con_head, as it points to the list of array candidates. We need to restore this before leaving this routine. */ saved_i_con_head = i_con_head; i_con_head = NULL; /* set up raid lists */ prot_level_list = malloc(sizeof(struct prot_level)); prot_level_list[raid_index].is_valid = 0; for_each_supported_arrays_rcd(supported_arrays, &qac) { for_each_cap_entry(cap_entry, supported_arrays) { prot_level_list[raid_index].array_cap_entry = cap_entry; prot_level_list[raid_index].is_valid = 1; if (!strcmp((char *)cap_entry->prot_level_str, dev->prot_level_str)) raid_index_default = raid_index; raid_index++; prot_level_list = realloc(prot_level_list, sizeof(struct prot_level) * (raid_index + 1)); prot_level_list[raid_index].is_valid = 0; } } if (!raid_index) { /* We should never get here. */ free(prot_level_list); scsi_err(dev, "The query data for the array indicate " "that there are no supported RAID levels.\n"); return RC_80_Migrate_Prot_Fail | EXIT_FLAG; } /* Title */ input_fields[0] = new_field(1, max_x - start_x, /* new field size */ 0, 0, /* upper left corner */ 0, /* number of offscreen rows */ 0); /* number of working buffers */ /* Protection Level */ input_fields[1] = new_field(1, 9, /* new field size */ 8, 44, /* */ 0, /* number of offscreen rows */ 0); /* number of working buffers */ input_fields[2] = NULL; raid_index = raid_index_default; cap_entry = prot_level_list[raid_index].array_cap_entry; form = new_form(input_fields); set_current_field(form, input_fields[1]); set_field_just(input_fields[0], JUSTIFY_CENTER); field_opts_off(input_fields[0], O_ACTIVE); field_opts_off(input_fields[1], O_EDIT); set_field_buffer(input_fields[0], /* field to alter */ 0, /* number of buffer to alter */ _("Select Protection Level")); /* string value to set */ form_opts_off(form, O_BS_OVERLOAD); flush_stdscr(); clear(); set_form_win(form,stdscr); set_form_sub(form,stdscr); post_form(form); mvaddstr(2, 1, _("Current RAID protection level is shown. To change")); mvaddstr(3, 1, _("setting hit \"c\" for options menu. Highlight desired")); mvaddstr(4, 1, _("option then hit Enter")); mvaddstr(6, 1, _("c=Change Setting")); sprintf(dev_str, "%s - Protection Level . . . . . . :", dev->dev_name); mvaddstr(8, 0, dev_str); mvaddstr(max_y - 4, 0, _("Press Enter to Continue")); mvaddstr(max_y - 2, 0, EXIT_KEY_LABEL CANCEL_KEY_LABEL); form_driver(form, /* form to pass input to */ REQ_FIRST_FIELD ); /* form request code */ sprintf(raid_str, "RAID %s", dev->prot_level_str); while (1) { set_field_buffer(input_fields[1], /* field to alter */ 0, /* number of buffer to alter */ raid_str); /* string value to set */ refresh(); ch = getch(); if (IS_EXIT_KEY(ch)) { rc = EXIT_FLAG; break; } else if (IS_CANCEL_KEY(ch)) { rc = CANCEL_FLAG; break; } else if (ch == 'c') { cur_field = current_field(form); cur_field_index = field_index(cur_field); if (cur_field_index == 1) { /* count the number of valid entries */ index = 0; while (prot_level_list[index].is_valid) index++; /* get appropriate memory, the text portion needs to be done up front as the new_item() function uses the passed pointer to display data */ raid_item = realloc(raid_item, sizeof(ITEM **) * (index + 1)); raid_menu_str = realloc(raid_menu_str, sizeof(struct text_str) * (index)); userptr = realloc(userptr, sizeof(int) * (index + 1)); /* set up protection level menu */ index = 0; while (prot_level_list[index].is_valid) { raid_item[index] = NULL; cap_entry = prot_level_list[index].array_cap_entry; sprintf(raid_menu_str[index].line, "RAID %s", cap_entry->prot_level_str); raid_item[index] = new_item(raid_menu_str[index].line,""); userptr[index] = index; set_item_userptr(raid_item[index], (char *)&userptr[index]); index++; } raid_item[index] = NULL; start_row = 8; /* select the desired new protection level */ rc = display_menu(raid_item, start_row, index, &retptr); if (rc == RC_SUCCESS) { raid_index = *retptr; cap_entry = prot_level_list[raid_index].array_cap_entry; sprintf(raid_str, "RAID %s", cap_entry->prot_level_str); } /* clean up */ for (i=0; raid_item[i] != NULL; i++) free_item(raid_item[i]); free(raid_item); raid_item = NULL; } else continue; } else if (IS_ENTER_KEY(ch)) { /* don't change anything if Cancel or Exit */ if (rc != RC_SUCCESS) break; /* set protection level */ cur_raid_cmd->prot_level = cap_entry->prot_level; /* are additional devices needed? */ if (cap_entry->format_overlay_type == IPR_FORMAT_ADD_DEVICES) { rc = choose_migrate_disks(ioa, &qac, cap_entry); if (rc) break; } else if (cap_entry->format_overlay_type != IPR_FORMAT_REMOVE_DEVICES) { /* We should never get here. */ scsi_err(dev, "The capability entry for the array " "does not have a valid overlay type.\n"); return RC_80_Migrate_Prot_Fail | EXIT_FLAG; } rc = confirm_raid_migrate(); if (rc) break; /* set up qac and send command to adapter */ rc = do_ipr_migrate_array_protection(array_i_con, ioa, &qac, dev->stripe_size, cur_raid_cmd->prot_level, cur_raid_cmd->array_id); break; } else if (ch == KEY_RIGHT) form_driver(form, REQ_NEXT_CHAR); else if (ch == KEY_LEFT) form_driver(form, REQ_PREV_CHAR); else if ((ch == KEY_BACKSPACE) || (ch == 127)) form_driver(form, REQ_DEL_PREV); else if (ch == KEY_DC) form_driver(form, REQ_DEL_CHAR); else if (ch == KEY_IC) form_driver(form, REQ_INS_MODE); else if (ch == KEY_EIC) form_driver(form, REQ_OVL_MODE); else if (ch == KEY_HOME) form_driver(form, REQ_BEG_FIELD); else if (ch == KEY_END) form_driver(form, REQ_END_FIELD); else if (ch == '\t') form_driver(form, REQ_NEXT_FIELD); else if (ch == KEY_UP) form_driver(form, REQ_PREV_FIELD); else if (ch == KEY_DOWN) form_driver(form, REQ_NEXT_FIELD); else if (isascii(ch)) form_driver(form, ch); } /* restore i_con_head */ i_con_head = saved_i_con_head; free(prot_level_list); free(raid_menu_str); free(userptr); unpost_form(form); free_form(form); free_screen(NULL, NULL, input_fields); flush_stdscr(); LEAVE; return rc; } /** * process_raid_migrate - Process the list of arrays that can be migrated. * @buffer: * @header_lines: * @last_array_cmd: * * Returns: * 0 if success / non-zero on failure **/ int process_raid_migrate(char *buffer[], int header_lines) { struct array_cmd_data *acd, *saved_cmd_head; struct array_cmd_data *last_array_cmd; struct screen_output *s_out; int found = 0; int toggle = 1; i_container *temp_i_con; char *input; int rc = REFRESH_FLAG; ENTER; while (rc & REFRESH_FLAG) { toggle_field = 0; do { n_raid_migrate.body = buffer[toggle&1]; /* display disk array selection screen */ s_out = screen_driver(&n_raid_migrate, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); if (rc & EXIT_FLAG || rc & CANCEL_FLAG) break; found = 0; /* Only one migration is allowed at a time. Set the do_cmd * flag for the first one that is found. */ for_each_icon(temp_i_con) { acd = temp_i_con->data; input = temp_i_con->field_data; if (acd && strcmp(input, "1") == 0) { found++; acd->do_cmd = 1; break; } } if (found) { last_array_cmd = raid_cmd_tail; /* Go to protection level selection screen */ rc = configure_raid_migrate(temp_i_con); /* Keep the array information in the list, but * strip off any disk inforamtion. */ saved_cmd_head = raid_cmd_head; raid_cmd_head = last_array_cmd->next; free_raid_cmds(); raid_cmd_head = saved_cmd_head; raid_cmd_tail = last_array_cmd; last_array_cmd->next = NULL; /* Done on success or exit, otherwise refresh */ if (rc == RC_SUCCESS || rc & EXIT_FLAG) break; /* clear the do_cmd flag */ acd->do_cmd = 0; rc = REFRESH_FLAG; } else { s_status.index = INVALID_OPTION_STATUS; rc = REFRESH_FLAG; } toggle++; } LEAVE; return rc; } /** * raid_migrate - GUI routine for migrate raid array protection. * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int raid_migrate(i_container *i_con) { int rc, k; int found = 0; struct ipr_dev *array, *dev; char *buffer[2]; struct ipr_ioa *ioa; struct screen_output *s_out; int header_lines; ENTER; processing(); /* make sure the i_con list is empty */ i_con = free_i_con(i_con); rc = RC_SUCCESS; check_current_config(false); body_init_status(buffer, n_raid_migrate.header, &header_lines); for_each_primary_ioa(ioa) { for_each_array(ioa, array) { dev = array; if (!dev->array_rcd->migrate_cand) continue; if (ioa->sis64) dev = get_vset_from_array(ioa, dev); add_raid_cmd_tail(ioa, dev, dev->array_id); i_con = add_i_con(i_con, "\0", raid_cmd_tail); print_dev(k, dev, buffer, "%1", 2+k); found++; } } if (!found) { n_raid_migrate_fail.body = body_init(n_raid_migrate_fail.header, NULL); s_out = screen_driver(&n_raid_migrate_fail, 0, i_con); free(n_raid_migrate_fail.body); n_raid_migrate_fail.body = NULL; rc = s_out->rc | CANCEL_FLAG; free(s_out); } else rc = process_raid_migrate(buffer, header_lines); /* Display a progress screen. */ if (rc == 0) return raid_migrate_complete(); /* Free the remaining raid commands. */ free_raid_cmds(); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_raid_migrate.body = NULL; LEAVE; return rc; } /** * asym_access_menu - Display the Optimized and Non-Optimized choices. * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int asym_access_menu(i_container *i_con) { struct array_cmd_data *acd; int start_row; int num_menu_items; int menu_index = 0; ITEM ** menu_item = NULL; int *userptr = NULL; int *new_state; int rc; acd = (struct array_cmd_data *)i_con->data; start_row = i_con->y + 2; num_menu_items = 3; menu_item = malloc(sizeof(ITEM **) * (num_menu_items + 1)); userptr = malloc(sizeof(int) * num_menu_items); menu_item[menu_index] = new_item("Optimized",""); userptr[menu_index] = 0; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = new_item("Non-Optimized",""); userptr[menu_index] = 1; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = new_item("Not Set",""); userptr[menu_index] = 2; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = NULL; rc = display_menu(menu_item, start_row, menu_index, &new_state); if (rc == RC_SUCCESS) { /* set field_data based on menu choice */ if (*new_state == IPR_ACTIVE_OPTIMIZED) sprintf(i_con->field_data, "Optimized"); else if (*new_state == IPR_ACTIVE_NON_OPTIMIZED) sprintf(i_con->field_data, "Non-Optimized"); else if (*new_state == IPR_ACTIVE_STANDBY) sprintf(i_con->field_data, "Not Set"); /* only issue the command if the state selection changed */ if (acd->dev->array_rcd->saved_asym_access_state != *new_state) acd->do_cmd = 1; else acd->do_cmd = 0; } else { /* set field_data based on existing value */ if (acd->dev->array_rcd->saved_asym_access_state == IPR_ACTIVE_OPTIMIZED) sprintf(i_con->field_data, "Optimized"); else if (acd->dev->array_rcd->saved_asym_access_state == IPR_ACTIVE_NON_OPTIMIZED) sprintf(i_con->field_data, "Non-Optimized"); else sprintf(i_con->field_data, "Not Set"); } menu_index = 0; while (menu_item[menu_index] != NULL) free_item(menu_item[menu_index++]); free(menu_item); free(userptr); menu_item = NULL; return rc; } /** * configure_asym_access - Configure the array setting for asymmetric access. * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int configure_asym_access(struct array_cmd_data *acd) { i_container *new_i_con, *i_con_head_saved; int header_lines = 0; char pref_str[20]; char buffer[128]; char *body = NULL; struct screen_output *s_out; int state = 0, rc = REFRESH_FLAG; i_con_head_saved = i_con_head; i_con_head = new_i_con = NULL; body = body_init(n_change_array_asym_access.header, &header_lines); sprintf(buffer, "Array: %s", acd->dev->dev_name); body = add_line_to_body(body, buffer, NULL); if (acd->dev->array_rcd->current_asym_access_state == IPR_ACTIVE_OPTIMIZED) sprintf(buffer, "Current asymmetric access state: Optimized"); else sprintf(buffer, "Current asymmetric access state: Non-Optimized"); body = add_line_to_body(body, buffer, NULL); if (acd->dev->array_rcd->saved_asym_access_state == IPR_ACTIVE_OPTIMIZED) { sprintf(buffer, "Saved asymmetric access state: Optimized\n"); sprintf(pref_str, "Optimized"); } else if (acd->dev->array_rcd->saved_asym_access_state == IPR_ACTIVE_NON_OPTIMIZED) { sprintf(buffer, "Saved asymmetric access state: Non-Optimized\n"); sprintf(pref_str, "Non-Optimized"); } else { sprintf(buffer, "Saved asymmetric access state: Not Set\n"); sprintf(pref_str, "Not Set"); } body = add_line_to_body(body, buffer, NULL); header_lines += 2; body = add_line_to_body(body,_("Preferred Asymmetric Access State"), "%13"); new_i_con = add_i_con(new_i_con, pref_str, acd); n_change_array_asym_access.body = body; while (rc & REFRESH_FLAG) { s_out = screen_driver(&n_change_array_asym_access, header_lines, NULL); rc = s_out->rc; free(s_out); if (rc == RC_SUCCESS) { if (!acd->do_cmd) { rc = REFRESH_FLAG; break; } if (!strncmp(new_i_con->field_data, "Optimized", 8)) state = IPR_ACTIVE_OPTIMIZED; else if (!strncmp(new_i_con->field_data, "Non-Optimized", 8)) state = IPR_ACTIVE_NON_OPTIMIZED; else state = IPR_ACTIVE_STANDBY; acd->dev->array_rcd->issue_cmd = 1; acd->dev->array_rcd->saved_asym_access_state = state; rc = ipr_set_array_asym_access(acd->ioa); } } processing(); free(n_change_array_asym_access.body); n_change_array_asym_access.body = NULL; free_i_con(new_i_con); i_con_head = i_con_head_saved; return rc; } /** * process_asym_access - Process the arrays that support asymmetric access. * @buffer: * @header_lines: * * Returns: * 0 if success / non-zero on failure **/ int process_asym_access(char *buffer[], int header_lines) { struct screen_output *s_out; int found = 0; int toggle = 1; i_container *i_con; char *input; int rc; struct array_cmd_data *acd; while (1) { toggle_field = 0; do { n_asym_access.body = buffer[toggle&1]; /* display array selection screen */ s_out = screen_driver(&n_asym_access, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); if (rc & EXIT_FLAG || rc & CANCEL_FLAG) break; found = 0; /* do one at a time */ for_each_icon(i_con) { acd = i_con->data; input = i_con->field_data; if (acd && strcmp(input, "1") == 0) { found++; break; } } if (found) { /* Go to asymmetric access selection screen */ rc = configure_asym_access(acd); /* Done on success or exit */ if (rc == RC_SUCCESS || rc & EXIT_FLAG) break; /* clear the do_cmd flag */ acd->do_cmd = 0; } else s_status.index = INVALID_OPTION_STATUS; } return rc; } /** * asym_access - GUI routine for setting array asymmetric access. * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int asym_access(i_container *i_con) { int rc, k; int found = 0; char *buffer[2]; struct screen_output *s_out; int header_lines; struct ipr_ioa *ioa; struct ipr_dev *array; processing(); /* make sure the i_con list is empty */ i_con = free_i_con(i_con); check_current_config(false); rc = RC_SUCCESS; body_init_status(buffer, n_asym_access.header, &header_lines); for_each_primary_ioa(ioa) { if (!ioa->asymmetric_access || !ioa->asymmetric_access_enabled) continue; for_each_array(ioa, array) { if (!array->array_rcd->asym_access_cand) { syslog_dbg("candidate not set\n"); continue; } add_raid_cmd_tail(ioa, array, array->array_id); i_con = add_i_con(i_con, "\0", raid_cmd_tail); print_dev(k, array, buffer, "%1", k+2); found++; } } if (!found) { n_asym_access_fail.body = body_init(n_asym_access_fail.header, NULL); s_out = screen_driver(&n_asym_access_fail, 0, i_con); free(n_asym_access_fail.body); n_asym_access_fail.body = NULL; rc = s_out->rc | CANCEL_FLAG; free(s_out); } else rc = process_asym_access(buffer, header_lines); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } return rc; } /** * raid_include - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int raid_include(i_container *i_con) { int k; int found = 0; struct ipr_array_record *array_rcd; char *buffer[2]; struct ipr_ioa *ioa; struct ipr_dev *dev; struct ipr_array_cap_entry *cap_entry; int rc = RC_SUCCESS; struct screen_output *s_out; int header_lines; int toggle = 1; processing(); i_con = free_i_con(i_con); check_current_config(false); body_init_status(buffer, n_raid_include.header, &header_lines); for_each_ioa(ioa) { if (ioa->is_secondary) continue; for_each_vset(ioa, dev) { array_rcd = dev->array_rcd; cap_entry = get_raid_cap_entry(ioa->supported_arrays, dev->raid_level); if (!cap_entry || !cap_entry->include_allowed || !array_rcd->established) continue; add_raid_cmd_tail(ioa, dev, dev->array_id); i_con = add_i_con(i_con,"\0",raid_cmd_tail); print_dev(k, dev, buffer, "%1", k); found++; } } if (!found) { /* Include Device Parity Protection Failed */ n_raid_include_fail.body = body_init(n_raid_include_fail.header, NULL); s_out = screen_driver(&n_raid_include_fail, 0, i_con); free(n_raid_include_fail.body); n_raid_include_fail.body = NULL; rc = s_out->rc; free(s_out); } else { toggle_field = 0; do { n_raid_include.body = buffer[toggle&1]; s_out = screen_driver(&n_raid_include, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); free_raid_cmds(); } for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_raid_include.body = NULL; return rc; } /** * configure_raid_include - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME * INVALID_OPTION_STATUS if nothing to process **/ int configure_raid_include(i_container *i_con) { int k; int found = 0; struct array_cmd_data *cur_raid_cmd = NULL; struct ipr_array_query_data *qac_data = calloc(1,sizeof(struct ipr_array_query_data)); struct ipr_dev_record *dev_rcd; struct scsi_dev_data *scsi_dev_data; char *buffer[2]; struct ipr_ioa *ioa; char *input; struct ipr_dev *vset, *dev; struct ipr_array_cap_entry *cap_entry; u8 min_mult_array_devices = 1; int rc = RC_SUCCESS; int header_lines; i_container *temp_i_con; int toggle = 0; struct screen_output *s_out; for_each_icon(temp_i_con) { cur_raid_cmd = temp_i_con->data; if (!cur_raid_cmd) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { found++; cur_raid_cmd->do_cmd = 1; break; } } if (found != 1) return INVALID_OPTION_STATUS; i_con = free_i_con(i_con); body_init_status(buffer, n_configure_raid_include.header, &header_lines); ioa = cur_raid_cmd->ioa; vset = cur_raid_cmd->dev; /* Get Query Array Config Data */ rc = ipr_query_array_config(ioa, 0, 1, 0, cur_raid_cmd->array_id, qac_data); found = 0; if (rc != 0) free(qac_data); else { cur_raid_cmd->qac = qac_data; cap_entry = get_raid_cap_entry(ioa->supported_arrays, vset->raid_level); if (cap_entry) min_mult_array_devices = cap_entry->min_mult_array_devices; for_each_dev_rcd(dev_rcd, qac_data) { for_each_dev(ioa, dev) { scsi_dev_data = dev->scsi_dev_data; if (!scsi_dev_data) continue; if (scsi_dev_data->handle == ipr_get_dev_res_handle(ioa, dev_rcd) && scsi_dev_data->opens == 0 && dev_rcd->include_cand && device_supported(dev)) { dev->dev_rcd = dev_rcd; i_con = add_i_con(i_con, "\0", dev); print_dev(k, dev, buffer, "%1", k); found++; break; } } } } if (found < min_mult_array_devices) { /* Include Device Parity Protection Failed */ n_configure_raid_include_fail.body = body_init(n_configure_raid_include_fail.header, NULL); s_out = screen_driver(&n_configure_raid_include_fail, 0, i_con); free(n_configure_raid_include_fail.body); n_configure_raid_include_fail.body = NULL; rc = s_out->rc; free(s_out); return rc; } toggle_field = 0; do { n_configure_raid_include.body = buffer[toggle&1]; s_out = screen_driver(&n_configure_raid_include, header_lines, i_con); temp_i_con = s_out->i_con; rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_configure_raid_include.body = NULL; i_con = temp_i_con; if (rc > 0) goto leave; /* first, count devices selected to be sure min multiple is satisfied */ found = 0; for_each_icon(temp_i_con) { input = temp_i_con->field_data; if (strcmp(input, "1") == 0) found++; } if (found % min_mult_array_devices != 0) { /* "Error: number of devices selected must be a multiple of %d" */ s_status.num = min_mult_array_devices; return 25 | REFRESH_FLAG; } for_each_icon(temp_i_con) { dev = (struct ipr_dev *)temp_i_con->data; if (!dev) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { if (!dev->dev_rcd->issue_cmd) { dev->dev_rcd->issue_cmd = 1; dev->dev_rcd->known_zeroed = 1; } } else { if (dev->dev_rcd->issue_cmd) { dev->dev_rcd->issue_cmd = 0; dev->dev_rcd->known_zeroed = 0; } } } rc = confirm_raid_include(i_con); leave: return rc; } /** * confirm_raid_include - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int confirm_raid_include(i_container *i_con) { struct array_cmd_data *cur_raid_cmd; struct ipr_ioa *ioa; struct ipr_dev *dev; int k; char *buffer[2]; int rc = RC_SUCCESS; struct screen_output *s_out; int num_devs = 0; int dev_include_complete(u8 num_devs); int format_include_cand(); int header_lines; int toggle = 0; int need_formats = 0; body_init_status(buffer, n_confirm_raid_include.header, &header_lines); for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->qac) continue; ioa = cur_raid_cmd->ioa; for_each_af_dasd(ioa, dev) { if (!dev->dev_rcd->issue_cmd) continue; print_dev(k, dev, buffer, "1", k); if (!ipr_device_is_zeroed(dev)) need_formats = 1; num_devs++; } } do { n_confirm_raid_include.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_raid_include, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_raid_include.body = NULL; if (rc) return rc; /* now format each of the devices */ if (need_formats) num_devs = format_include_cand(); rc = dev_include_complete(num_devs); free_devs_to_init(); return rc; } /** * format_include_cand - * * Returns: * number of devices processed **/ int format_include_cand() { struct scsi_dev_data *scsi_dev_data; struct array_cmd_data *cur_raid_cmd; struct ipr_ioa *ioa; struct ipr_dev *dev; int num_devs = 0; int rc = 0; struct ipr_dev_record *device_record; int opens = 0; for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; ioa = cur_raid_cmd->ioa; for_each_dev(ioa, dev) { device_record = dev->dev_rcd; if (!device_record || !device_record->issue_cmd) continue; /* get current "opens" data for this device to determine conditions to continue */ scsi_dev_data = dev->scsi_dev_data; if (scsi_dev_data) opens = num_device_opens(scsi_dev_data->host, scsi_dev_data->channel, scsi_dev_data->id, scsi_dev_data->lun); if (opens != 0) { syslog(LOG_ERR,_("Include Device not allowed, device in use - %s\n"), dev->gen_name); device_record->issue_cmd = 0; continue; } if (dev_init_head) { dev_init_tail->next = malloc(sizeof(struct devs_to_init_t)); dev_init_tail = dev_init_tail->next; } else dev_init_head = dev_init_tail = malloc(sizeof(struct devs_to_init_t)); memset(dev_init_tail, 0, sizeof(struct devs_to_init_t)); dev_init_tail->ioa = ioa; dev_init_tail->dev = dev; dev_init_tail->do_init = 1; /* Issue the format. Failure will be detected by query command status */ rc = ipr_format_unit(dev); num_devs++; } } return num_devs; } /** * update_include_qac_data - * @ioa: ipr ioa struct * @old_qac: ipr_array_query_data struct * @new_qac: ipr_array_query_data struct * * Returns: * 0 if success / 26 on failure **/ static int update_include_qac_data(struct ipr_ioa *ioa, struct ipr_array_query_data *old_qac, struct ipr_array_query_data *new_qac) { struct ipr_dev_record *old_dev_rcd, *new_dev_rcd; int found; for_each_dev_rcd(old_dev_rcd, old_qac) { if (!old_dev_rcd->issue_cmd) continue; found = 0; for_each_dev_rcd(new_dev_rcd, new_qac) { if (ipr_get_dev_res_handle(ioa, new_dev_rcd) != ipr_get_dev_res_handle(ioa, old_dev_rcd)) continue; new_dev_rcd->issue_cmd = 1; new_dev_rcd->known_zeroed = 1; found = 1; break; } if (!found) return 26; } return 0; } /** * do_array_include - * @ioa: ipr ioa struct * @array_id: array ID * @qac: ipr_array_query_data struct * * Returns: * 0 if success / non-zero on failure **/ static int do_array_include(struct ipr_ioa *ioa, int array_id, struct ipr_array_query_data *qac) { int fd, rc; struct ipr_array_query_data qac_data; memset(&qac_data, 0, sizeof(qac_data)); fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) return -EIO; rc = flock(fd, LOCK_EX); if (rc) goto out; rc = __ipr_query_array_config(ioa, fd, 0, 1, 0, array_id, &qac_data); if (rc) goto out; rc = update_include_qac_data(ioa, qac, &qac_data); if (rc) goto out; rc = ipr_add_array_device(ioa, fd, &qac_data); out: close(fd); return rc; } /** * dev_include_complete - * @num_devs: not used ? * * Returns: * 27 | EXIT_FLAG on success / 26 | EXIT_FLAG otherwise **/ int dev_include_complete(u8 num_devs) { int done_bad; struct ipr_cmd_status cmd_status; struct ipr_cmd_status_record *status_record; int not_done = 0; int rc; struct devs_to_init_t *cur_dev_init; u32 percent_cmplt = 0; struct ipr_ioa *ioa; struct array_cmd_data *cur_raid_cmd; struct ipr_common_record *common_record; n_dev_include_complete.body = n_dev_include_complete.header[0]; while(1) { complete_screen_driver(&n_dev_include_complete, percent_cmplt, 0); percent_cmplt = 100; done_bad = 0; for_each_dev_to_init(cur_dev_init) { if (cur_dev_init->do_init) { rc = ipr_query_command_status(cur_dev_init->dev, &cmd_status); if (rc || cmd_status.num_records == 0) { cur_dev_init->cmplt = 100; continue; } status_record = cmd_status.record; if (status_record->status == IPR_CMD_STATUS_FAILED) { cur_dev_init->cmplt = 100; done_bad = 1; } else if (status_record->status != IPR_CMD_STATUS_SUCCESSFUL) { cur_dev_init->cmplt = status_record->percent_complete; if (cur_dev_init->cmplt < percent_cmplt) percent_cmplt = cur_dev_init->cmplt; not_done = 1; } } } if (!not_done) { flush_stdscr(); if (done_bad) { /* "Include failed" */ rc = 26; return rc | EXIT_FLAG; } break; } not_done = 0; sleep(1); } complete_screen_driver(&n_dev_include_complete, percent_cmplt, 0); /* now issue the start array command with "known to be zero" */ for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; if (do_array_include(cur_raid_cmd->ioa, cur_raid_cmd->array_id, cur_raid_cmd->qac)) { rc = 26; continue; } } not_done = 0; percent_cmplt = 0; n_dev_include_complete.body = n_dev_include_complete.header[1]; while(1) { complete_screen_driver(&n_dev_include_complete, percent_cmplt, 1); percent_cmplt = 100; done_bad = 0; for_each_raid_cmd(cur_raid_cmd) { ioa = cur_raid_cmd->ioa; rc = ipr_query_command_status(&ioa->ioa, &cmd_status); if (rc) { done_bad = 1; continue; } for_each_cmd_status(status_record, &cmd_status) { if (status_record->command_code == IPR_ADD_ARRAY_DEVICE && cur_raid_cmd->array_id == status_record->array_id) { if (status_record->status == IPR_CMD_STATUS_IN_PROGRESS) { if (status_record->percent_complete < percent_cmplt) percent_cmplt = status_record->percent_complete; not_done = 1; } else if (status_record->status != IPR_CMD_STATUS_SUCCESSFUL) /* "Include failed" */ rc = RC_26_Include_Fail; break; } } } if (!not_done) { /* Call ioctl() to re-read partition table to handle change in size */ for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; common_record = cur_raid_cmd->dev->qac_entry; if (common_record && common_record->record_id == IPR_RECORD_ID_ARRAY_RECORD) rc = ipr_write_dev_attr(cur_raid_cmd->dev, "rescan", "1"); } flush_stdscr(); if (done_bad) /* "Include failed" */ return RC_26_Include_Fail | EXIT_FLAG; /* "Include Unit completed successfully" */ complete_screen_driver(&n_dev_include_complete, percent_cmplt, 1); return RC_27_Include_Success | EXIT_FLAG; } not_done = 0; sleep(1); } } /** * add_format_device - * @dev: ipr dev struct * @blk_size: block size * * Returns: * nothing **/ static void add_format_device(struct ipr_dev *dev, int blk_size) { if (dev_init_head) { dev_init_tail->next = malloc(sizeof(struct devs_to_init_t)); dev_init_tail = dev_init_tail->next; } else dev_init_head = dev_init_tail = malloc(sizeof(struct devs_to_init_t)); memset(dev_init_tail, 0, sizeof(struct devs_to_init_t)); dev_init_tail->ioa = dev->ioa; if (ipr_is_af_dasd_device(dev)) dev_init_tail->dev_type = IPR_AF_DASD_DEVICE; else dev_init_tail->dev_type = IPR_JBOD_DASD_DEVICE; dev_init_tail->new_block_size = blk_size; dev_init_tail->dev = dev; } /** * configure_af_device - * @i_con: i_container struct * @action_code: include or remove * * Returns: * 0 if success / non-zero on failure **/ static int configure_af_device(i_container *i_con, int action_code) { int rc, j, k; struct scsi_dev_data *scsi_dev_data; int can_init; int dev_type; int new_block_size; char *buffer[2]; int num_devs = 0; struct ipr_ioa *ioa; int toggle = 0; int header_lines; s_node *n_screen; struct screen_output *s_out; processing(); i_con = free_i_con(i_con); rc = RC_SUCCESS; check_current_config(false); /* Setup screen title */ if (action_code == IPR_INCLUDE) n_screen = &n_af_init_device; else n_screen = &n_jbod_init_device; body_init_status(buffer, n_screen->header, &header_lines); for_each_ioa(ioa) { if (ioa->is_secondary) continue; for (j = 0; j < ioa->num_devices; j++) { can_init = 1; scsi_dev_data = ioa->dev[j].scsi_dev_data; /* If not a DASD, disallow format */ if (!scsi_dev_data || ipr_is_hot_spare(&ioa->dev[j]) || ipr_is_volume_set(&ioa->dev[j]) || !device_supported(&ioa->dev[j]) || (scsi_dev_data->type != TYPE_DISK && scsi_dev_data->type != IPR_TYPE_AF_DISK)) continue; /* If Advanced Function DASD */ if (ipr_is_af_dasd_device(&ioa->dev[j])) { if (action_code == IPR_INCLUDE) { if (ipr_device_is_zeroed(&ioa->dev[j])) continue; new_block_size = 0; } else if (ioa->dev[j].block_dev_class & IPR_BLK_DEV_CLASS_4K) new_block_size = IPR_JBOD_4K_BLOCK_SIZE; else new_block_size = IPR_JBOD_BLOCK_SIZE; dev_type = IPR_AF_DASD_DEVICE; /* We allow the user to format the drive if nobody is using it */ if (ioa->dev[j].scsi_dev_data->opens != 0) { syslog(LOG_ERR, _("Format not allowed to %s, device in use\n"), ioa->dev[j].gen_name); continue; } if (ipr_is_array_member(&ioa->dev[j])) { if (ioa->dev[j].dev_rcd->no_cfgte_vol) can_init = 1; else can_init = 0; } else can_init = is_format_allowed(&ioa->dev[j]); } else if (scsi_dev_data->type == TYPE_DISK){ /* If on a JBOD adapter */ if (!ioa->qac_data->num_records) { if (action_code != IPR_REMOVE) continue; } else if (is_af_blocked(&ioa->dev[j], 0)) continue; if (action_code == IPR_REMOVE && !format_req(&ioa->dev[j])) continue; if (action_code == IPR_REMOVE) { if (ioa->support_4k && ioa->dev[j].block_dev_class & IPR_BLK_DEV_CLASS_4K) new_block_size = IPR_JBOD_4K_BLOCK_SIZE; else new_block_size = IPR_JBOD_BLOCK_SIZE; } else { if (ioa->support_4k && ioa->dev[j].block_dev_class & IPR_BLK_DEV_CLASS_4K) new_block_size = IPR_AF_4K_BLOCK_SIZE; else new_block_size = ioa->af_block_size; } dev_type = IPR_JBOD_DASD_DEVICE; /* We allow the user to format the drive if nobody is using it, or the device is read write protected. If the drive fails, then is replaced concurrently it will be read write protected, but the system may still have a use count for it. We need to allow the format to get the device into a state where the system can use it */ if (ioa->dev[j].scsi_dev_data->opens != 0) { syslog(LOG_ERR, _("Format not allowed to %s, device in use\n"), ioa->dev[j].gen_name); continue; } can_init = is_format_allowed(&ioa->dev[j]); } else continue; if (can_init) { add_format_device(&ioa->dev[j], new_block_size); print_dev(k, &ioa->dev[j], buffer, "%1", k); i_con = add_i_con(i_con,"\0",dev_init_tail); num_devs++; } } } if (!num_devs) { if (action_code == IPR_INCLUDE) { n_af_include_fail.body = body_init(n_af_include_fail.header, NULL); s_out = screen_driver(&n_af_include_fail, 0, i_con); free(n_af_include_fail.body); n_af_include_fail.body = NULL; } else { n_af_remove_fail.body = body_init(n_af_remove_fail.header, NULL); s_out = screen_driver(&n_af_remove_fail, 0, i_con); free(n_af_remove_fail.body); n_af_remove_fail.body = NULL; } rc = s_out->rc; free(s_out); } else { toggle_field = 0; do { n_screen->body = buffer[toggle&1]; s_out = screen_driver(n_screen, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); free_devs_to_init(); } for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_screen->body = NULL; return rc; } /** * af_include - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int af_include(i_container *i_con) { int rc; rc = configure_af_device(i_con, IPR_INCLUDE); return rc; } /** * af_remove - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int af_remove(i_container *i_con) { int rc; rc = configure_af_device(i_con, IPR_REMOVE); return rc; } /** * add_hot_spare - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int add_hot_spare(i_container *i_con) { int rc; do { rc = hot_spare(i_con, IPR_ADD_HOT_SPARE); } while (rc & REFRESH_FLAG); return rc; } /** * remove_hot_spare - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int remove_hot_spare(i_container *i_con) { int rc; do { rc = hot_spare(i_con, IPR_RMV_HOT_SPARE); } while (rc & REFRESH_FLAG); return rc; } /** * hot_spare - * @i_con: i_container struct * @action: add or remove * * Returns: * 0 if success / non-zero on failure FIXME **/ int hot_spare(i_container *i_con, int action) { int rc, k; struct array_cmd_data *cur_raid_cmd; char *buffer[2]; struct ipr_ioa *ioa; struct ipr_dev *dev; int found = 0; char *input; int toggle = 0; i_container *temp_i_con; struct screen_output *s_out; s_node *n_screen; int header_lines; processing(); i_con = free_i_con(i_con); rc = RC_SUCCESS; check_current_config(false); if (action == IPR_ADD_HOT_SPARE) n_screen = &n_add_hot_spare; else n_screen = &n_remove_hot_spare; body_init_status(buffer, n_screen->header, &header_lines); for_each_ioa(ioa) { for_each_af_dasd(ioa, dev) { if ((action == IPR_ADD_HOT_SPARE && dev->dev_rcd->add_hot_spare_cand) || (action == IPR_RMV_HOT_SPARE && dev->dev_rcd->rmv_hot_spare_cand)) { add_raid_cmd_tail(ioa, NULL, 0); i_con = add_i_con(i_con,"\0",raid_cmd_tail); print_dev(k, &ioa->ioa, buffer, "%1", k); found++; break; } } } if (!found) { if (action == IPR_ADD_HOT_SPARE) { n_add_hot_spare_fail.body = body_init(n_add_hot_spare_fail.header, NULL); s_out = screen_driver(&n_add_hot_spare_fail, 0, i_con); free(n_add_hot_spare_fail.body); n_add_hot_spare_fail.body = NULL; } else { n_remove_hot_spare_fail.body = body_init(n_remove_hot_spare_fail.header, NULL); s_out = screen_driver(&n_remove_hot_spare_fail, 0, i_con); free(n_remove_hot_spare_fail.body); n_remove_hot_spare_fail.body = NULL; } free(s_out); rc = EXIT_FLAG; goto leave; } do { n_screen->body = buffer[toggle&1]; s_out = screen_driver(n_screen, header_lines, i_con); temp_i_con = s_out->i_con; rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); i_con = temp_i_con; if (rc > 0) { rc = EXIT_FLAG; goto leave; } found = 0; for_each_icon(temp_i_con) { cur_raid_cmd = (struct array_cmd_data *)temp_i_con->data; if (cur_raid_cmd) { input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { do { rc = select_hot_spare(temp_i_con, action); } while (rc & REFRESH_FLAG); if (rc > 0) goto leave; found = 1; cur_raid_cmd->do_cmd = 1; } else { cur_raid_cmd->do_cmd = 0; } } } if (found) { /* Go to verification screen */ rc = confirm_hot_spare(action); } else rc = REFRESH_FLAG | INVALID_OPTION_STATUS; leave: if (rc & CANCEL_FLAG) { s_status.index = (rc & ~CANCEL_FLAG); rc = (rc | REFRESH_FLAG) & ~CANCEL_FLAG; } i_con = free_i_con(i_con); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_screen->body = NULL; free_raid_cmds(); return rc; } /** * select_hot_spare - * @i_con: i_container struct * @action: add or remove * * Returns: * 0 if success / non-zero on failure FIXME **/ int select_hot_spare(i_container *i_con, int action) { struct array_cmd_data *cur_raid_cmd; int rc, k, found; char *buffer[2]; int num_devs = 0; struct ipr_ioa *ioa; char *input; struct ipr_dev *dev; struct scsi_dev_data *scsi_dev_data; struct ipr_dev_record *dev_rcd; i_container *temp_i_con,*i_con2 = NULL; i_container *temp_i_con2; struct screen_output *s_out = NULL; int header_lines; s_node *n_screen; int toggle = 0; i_container *i_con_head_saved; if (action == IPR_ADD_HOT_SPARE) n_screen = &n_select_add_hot_spare; else n_screen = &n_select_remove_hot_spare; body_init_status(buffer, n_screen->header, &header_lines); cur_raid_cmd = (struct array_cmd_data *) i_con->data; rc = RC_SUCCESS; ioa = cur_raid_cmd->ioa; i_con_head_saved = i_con_head; /* FIXME */ i_con_head = NULL; for_each_af_dasd(ioa, dev) { scsi_dev_data = dev->scsi_dev_data; dev_rcd = dev->dev_rcd; if (!scsi_dev_data) continue; if (device_supported(dev) && ((dev_rcd->add_hot_spare_cand && action == IPR_ADD_HOT_SPARE) || (dev_rcd->rmv_hot_spare_cand && action == IPR_RMV_HOT_SPARE))) { i_con2 = add_i_con(i_con2, "\0", dev); print_dev(k, dev, buffer, "%1", k); num_devs++; } } if (num_devs) { do { n_screen->body = buffer[toggle&1]; s_out = screen_driver(n_screen, header_lines, i_con2); temp_i_con2 = s_out->i_con; rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_screen->body = NULL; i_con2 = temp_i_con2; if (rc > 0) goto leave; found = 0; for_each_icon(temp_i_con) { dev = (struct ipr_dev *)temp_i_con->data; if (dev != NULL) { input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { found = 1; dev->dev_rcd->issue_cmd = 1; } } } if (found) rc = RC_SUCCESS; else { s_status.index = INVALID_OPTION_STATUS; rc = REFRESH_FLAG; } } else /* "No devices available for the selected hot spare operation" */ rc = 52 | CANCEL_FLAG; leave: i_con2 = free_i_con(i_con2); i_con_head = i_con_head_saved; return rc; } /** * confirm_hot_spare - * @action: add or remove * * Returns: * 0 if success / non-zero on failure FIXME **/ int confirm_hot_spare(int action) { struct array_cmd_data *cur_raid_cmd; struct ipr_ioa *ioa; struct ipr_dev *dev; int rc, k; char *buffer[2]; int hot_spare_complete(int action); struct screen_output *s_out; s_node *n_screen; int header_lines; int toggle = 0; rc = RC_SUCCESS; if (action == IPR_ADD_HOT_SPARE) n_screen = &n_confirm_add_hot_spare; else n_screen = &n_confirm_remove_hot_spare; body_init_status(buffer, n_screen->header, &header_lines); for_each_raid_cmd(cur_raid_cmd) { if (!cur_raid_cmd->do_cmd) continue; ioa = cur_raid_cmd->ioa; for_each_af_dasd(ioa, dev) { if (dev->dev_rcd->issue_cmd) print_dev(k, dev, buffer, "1", k); } } do { n_screen->body = buffer[toggle&1]; s_out = screen_driver(n_screen, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_screen->body = NULL; if(rc > 0) goto leave; rc = hot_spare_complete(action); leave: return rc; } /** * hot_spare_complete - * @action: add or remove * * Returns: * 0 if success / non-zero on failure FIXME **/ int hot_spare_complete(int action) { int rc; struct ipr_ioa *ioa; struct array_cmd_data *cmd; for_each_ioa(ioa) { for_each_raid_cmd(cmd) { if (cmd->do_cmd == 0 || ioa != cmd->ioa) continue; flush_stdscr(); if (action == IPR_ADD_HOT_SPARE) rc = ipr_add_hot_spare(ioa); else rc = ipr_remove_hot_spare(ioa); if (rc != 0) { if (action == IPR_ADD_HOT_SPARE) /* Enable Device as Hot Spare failed */ return 55 | EXIT_FLAG; else /* Disable Device as Hot Spare failed */ return 56 | EXIT_FLAG; } } } flush_stdscr(); if (action == IPR_ADD_HOT_SPARE) /* "Enable Device as Hot Spare Completed successfully" */ rc = 53 | EXIT_FLAG; else /* "Disable Device as Hot Spare Completed successfully" */ rc = 54 | EXIT_FLAG; return rc; } /** * disk_unit_recovery - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int disk_unit_recovery(i_container *i_con) { int rc; struct screen_output *s_out; int loop; for (loop = 0; loop < n_disk_unit_recovery.num_opts; loop++) { n_disk_unit_recovery.body = ipr_list_opts(n_disk_unit_recovery.body, n_disk_unit_recovery.options[loop].key, n_disk_unit_recovery.options[loop].list_str); } n_disk_unit_recovery.body = ipr_end_list(n_disk_unit_recovery.body); s_out = screen_driver(&n_disk_unit_recovery, 0, NULL); free(n_disk_unit_recovery.body); n_disk_unit_recovery.body = NULL; rc = s_out->rc; free(s_out); return rc; } /** * get_elem_status - * @dev: ipr dev struct * @ses: ipr dev struct * @ses_data: ipr_encl_status_ctl_pg struct * @ses_config: ipr_ses_config_pg struct * * Returns: * ipr_drive_elem_status struct if success / NULL otherwise **/ static struct ipr_drive_elem_status * get_elem_status(struct ipr_dev *dev, struct ipr_dev *ses, struct ipr_encl_status_ctl_pg *ses_data, struct ipr_ses_config_pg *ses_cfg) { struct ipr_ioa *ioa = dev->ioa; struct ipr_res_addr *ra; struct ipr_drive_elem_status *elem_status, *overall; int bus = ses->scsi_dev_data->channel; int box, slot, is_vses; if (ioa->hop_count == IPR_2BIT_HOP) box = ses->scsi_dev_data->phy_2bit_hop.box; else box = ses->scsi_dev_data->phy.box; if (ipr_receive_diagnostics(ses, 2, ses_data, sizeof(*ses_data))) return NULL; if (ipr_receive_diagnostics(ses, 1, ses_cfg, sizeof(*ses_cfg))) return NULL; overall = ipr_get_overall_elem(ses_data, ses_cfg); if (!ioa_is_spi(dev->ioa) && (overall->device_environment == 0)) is_vses = 1; else is_vses = 0; for_each_elem_status(elem_status, ses_data, ses_cfg) { slot = elem_status->slot_id; for_each_ra(ra, dev) { if (is_vses && (ra->bus != slot || ra->target != 0)) continue; if (ioa->hop_count == IPR_2BIT_HOP) { if (!is_vses && (ra->bus != bus || ra->phy_2bit_hop.box != box || ra->phy_2bit_hop.phy != slot)) continue; } else { if (!is_vses && (ra->bus != bus || ra->phy.box != box || ra->phy.phy != slot)) continue; } return elem_status; } } return NULL; } /** * get_elem_status_64bit - * @dev: ipr dev struct * @ses: ipr dev struct * @ses_data: ipr_encl_status_ctl_pg struct * @ses_config: ipr_ses_config_pg struct * * Returns: * ipr_drive_elem_status struct if success / NULL otherwise **/ static struct ipr_drive_elem_status * get_elem_status_64bit(struct ipr_dev *dev, struct ipr_dev *ses, struct ipr_encl_status_ctl_pg *ses_data, struct ipr_ses_config_pg *ses_cfg) { struct ipr_res_path *rp; struct ipr_drive_elem_status *elem_status; int slot, res_path_len, dev_slot, ses_path_len; if (ipr_receive_diagnostics(ses, 2, ses_data, sizeof(*ses_data))) return NULL; if (ipr_receive_diagnostics(ses, 1, ses_cfg, sizeof(*ses_cfg))) return NULL; ses_path_len = strlen(ses->res_path_name); res_path_len = strlen(dev->res_path_name); dev_slot = strtoul(dev->res_path_name + (res_path_len-2), NULL, 16); if (ses_path_len != res_path_len) return NULL; for_each_elem_status(elem_status, ses_data, ses_cfg) { slot = elem_status->slot_id; for_each_rp(rp, dev) { if (!memcmp(&rp->res_path_bytes, &ses->res_path[0], ses_path_len/3) && slot == dev_slot) return elem_status; } } return NULL; } /** * wait_for_new_dev - * @ioa: ipr ioa struct * @res_addr: ipr_res_addr struct * * Returns: * nothing **/ static void wait_for_new_dev(struct ipr_ioa *ioa, struct ipr_res_addr *res_addr) { int time = 12; struct ipr_dev *dev; struct ipr_res_addr_aliases aliases; struct ipr_res_addr *ra; ipr_query_res_addr_aliases(ioa, res_addr, &aliases); for_each_ra_alias(ra, &aliases) ipr_scan(ioa, ra->bus, ra->target, ra->lun); while (time--) { check_current_config(false); for_each_ra_alias(ra, &aliases) { if ((dev = get_dev_from_addr(ra))) { ipr_init_new_dev(dev); return; } } sleep(5); } } /** * get_dev_from_res_path - * @res_addr: ipr_res_addr struct * * Returns: * ipr_dev struct if success / NULL otherwise **/ struct ipr_dev *get_dev_from_res_path(struct ipr_res_path *res_path) { struct ipr_ioa *ioa; int j; struct scsi_dev_data *scsi_dev_data; char res_path_name[IPR_MAX_RES_PATH_LEN]; ipr_format_res_path(res_path->res_path_bytes, res_path_name, IPR_MAX_RES_PATH_LEN); for_each_primary_ioa(ioa) { for (j = 0; j < ioa->num_devices; j++) { scsi_dev_data = ioa->dev[j].scsi_dev_data; if (!scsi_dev_data) continue; if (!strncmp(scsi_dev_data->res_path, res_path_name, IPR_MAX_RES_PATH_LEN)) return &ioa->dev[j]; } } return NULL; } static void wait_for_new_dev_64bit(struct ipr_ioa *ioa, struct ipr_res_path *res_path) { int time = 12; struct ipr_dev *dev; struct ipr_res_path_aliases aliases; struct ipr_res_path *rp; ipr_query_res_path_aliases(ioa, res_path, &aliases); if (aliases.length < (4 + 2 * sizeof(struct ipr_res_path))) { aliases.length = 4 + 2 * sizeof(struct ipr_res_path); memcpy(&aliases.res_path[0], res_path, 2 * sizeof(struct ipr_res_path)); } ipr_scan(ioa, 0, -1, -1); while (time--) { check_current_config(false); for_each_rp_alias(rp, &aliases) { if ((dev = get_dev_from_res_path(rp))) { ipr_init_new_dev(dev); return; } } sleep(5); } } static struct ipr_dev *delete_secondary_sysfs_name(struct ipr_dev *delete_dev) { struct ipr_ioa *ioa; struct ipr_dev *dev, *ses; for_each_secondary_ioa(ioa) { for_each_ses(ioa, ses) { for_each_dev(ses->ioa, dev) { if (ipr_is_ses(dev)) continue; if (!strncmp(dev->res_path_name, delete_dev->res_path_name, strlen(delete_dev->res_path_name))) return dev; } } } return NULL; } int remove_or_add_back_device_64bit(struct ipr_dev *dev) { struct ipr_encl_status_ctl_pg ses_data; struct ipr_drive_elem_status *elem_status; struct ipr_ses_config_pg ses_cfg; int res_path_len, dev_slot; struct ipr_dev *sec_dev, *tmp_dev; char new_sysfs_res_path[IPR_MAX_RES_PATH_LEN]; int rc; res_path_len = strlen(dev->res_path_name); dev_slot = strtoul(dev->res_path_name + (res_path_len - 2), NULL, 16); if (!ipr_read_dev_attr(dev, "resource_path", new_sysfs_res_path, IPR_MAX_RES_PATH_LEN)) if (strncmp(dev->res_path_name, new_sysfs_res_path, sizeof(dev->res_path_name))) for_each_dev(dev->ioa, tmp_dev) if (!strncmp(tmp_dev->res_path_name, new_sysfs_res_path, sizeof(tmp_dev->res_path_name))) { dev = tmp_dev; break; } evaluate_device(dev, dev->ioa, 0); if (ipr_receive_diagnostics(dev->ses[0], 2, &ses_data, sizeof(ses_data))) return INVALID_OPTION_STATUS; if (ipr_receive_diagnostics(dev->ses[0], 1, &ses_cfg, sizeof(ses_cfg))) return INVALID_OPTION_STATUS; for_each_elem_status(elem_status, &ses_data, &ses_cfg) { if (elem_status->slot_id == dev_slot && elem_status->status == IPR_DRIVE_ELEM_STATUS_POPULATED ) { wait_for_new_dev_64bit(dev->ioa, &(dev->res_path[0])); rc = 0; break; } else if (elem_status->slot_id == dev_slot && elem_status->status == IPR_DRIVE_ELEM_STATUS_EMPTY ) { ipr_write_dev_attr(dev, "delete", "1"); ipr_del_zeroed_dev(dev); sec_dev = delete_secondary_sysfs_name(dev); if (sec_dev) { evaluate_device(sec_dev, dev->ioa, 0); ipr_write_dev_attr(sec_dev, "delete", "1"); ipr_del_zeroed_dev(sec_dev); } rc = 1; break; } }/*for_each_elem_status*/ return rc; } /** * process_conc_maint - * @i_con: i_container struct * @action: add or remove * * Returns: * 0 if success / non-zero on failure FIXME **/ int process_conc_maint(i_container *i_con, int action) { i_container *temp_i_con; int found = 0; struct ipr_dev *dev, *ses; char *input; struct ipr_ioa *ioa; int k, rc; struct ipr_dev_record *dev_rcd; struct ipr_encl_status_ctl_pg ses_data; struct ipr_ses_config_pg ses_cfg; struct ipr_drive_elem_status *elem_status, *overall; char *buffer[3]; int header_lines; int toggle=0; s_node *n_screen; struct screen_output *s_out; struct ipr_res_addr res_addr; struct ipr_res_path res_path[2]; int max_y, max_x; for_each_icon(temp_i_con) { input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { dev = (struct ipr_dev *)temp_i_con->data; if (dev != NULL) found++; } } if (found != 1) return INVALID_OPTION_STATUS; if (dev->ioa->sis64) memcpy(&res_path, &(dev->res_path), sizeof(struct ipr_res_path)*2); else memcpy(&res_addr, &(dev->res_addr), sizeof(res_addr)); if (ipr_is_af_dasd_device(dev) && (action == IPR_VERIFY_CONC_REMOVE || action == IPR_WAIT_CONC_REMOVE)) { /* issue suspend device bus to verify operation is allowable */ if (!ipr_can_remove_device(dev)) return INVALID_OPTION_STATUS; /* FIXME */ } else if (num_device_opens(res_addr.host, res_addr.bus, res_addr.target, res_addr.lun)) return INVALID_OPTION_STATUS; /* FIXME */ ioa = dev->ioa; ses = dev->ses[0]; if (ioa->sis64) elem_status = get_elem_status_64bit(dev, ses, &ses_data, &ses_cfg); else elem_status = get_elem_status(dev, ses, &ses_data, &ses_cfg); if (!elem_status) return INVALID_OPTION_STATUS; if ((action == IPR_VERIFY_CONC_REMOVE) || (action == IPR_VERIFY_CONC_ADD)) { elem_status->select = 1; elem_status->identify = 1; } else if (action == IPR_WAIT_CONC_REMOVE) { elem_status->select = 1; elem_status->remove = 1; elem_status->identify = 1; } else if (action == IPR_WAIT_CONC_ADD) { elem_status->select = 1; elem_status->insert = 1; elem_status->identify = 1; } overall = ipr_get_overall_elem(&ses_data, &ses_cfg); overall->select = 1; overall->insert = 0; overall->remove = 0; overall->identify = 0; if (action == IPR_WAIT_CONC_REMOVE || action == IPR_WAIT_CONC_ADD) overall->disable_resets = 1; if (action == IPR_VERIFY_CONC_REMOVE) n_screen = &n_verify_conc_remove; else if (action == IPR_VERIFY_CONC_ADD) n_screen = &n_verify_conc_add; else if (action == IPR_WAIT_CONC_REMOVE) n_screen = &n_wait_conc_remove; else n_screen = &n_wait_conc_add; processing(); for (k = 0; k < 3; k++) { buffer[k] = __body_init_status(n_screen->header, &header_lines, (k + 8)); buffer[k] = print_device(dev, buffer[k], "1", (k + 5)); } rc = ipr_send_diagnostics(ses, &ses_data, ntohs(ses_data.byte_count) + 4); if (rc) { for (k = 0; k < 3; k++) { free(buffer[k]); buffer[k] = NULL; } return 30 | EXIT_FLAG; } /* call screen driver */ do { n_screen->body = buffer[toggle%3]; s_out = screen_driver(n_screen, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 3; k++) { free(buffer[k]); buffer[k] = NULL; } n_screen->body = NULL; if (ioa->sis64) elem_status = get_elem_status_64bit(dev, ses, &ses_data, &ses_cfg); else elem_status = get_elem_status(dev, ses, &ses_data, &ses_cfg); if (!elem_status) return 30 | EXIT_FLAG;; /* turn light off flashing light */ overall = ipr_get_overall_elem(&ses_data, &ses_cfg); overall->select = 1; overall->disable_resets = 0; overall->insert = 0; overall->remove = 0; overall->identify = 0; elem_status->select = 1; elem_status->insert = 0; elem_status->remove = 0; elem_status->identify = 0; ipr_send_diagnostics(ses, &ses_data, ntohs(ses_data.byte_count) + 4); /* call function to complete conc maint */ if (!rc) { if (action == IPR_VERIFY_CONC_REMOVE) { rc = process_conc_maint(i_con, IPR_WAIT_CONC_REMOVE); if (!rc) { dev_rcd = dev->dev_rcd; getmaxyx(stdscr,max_y,max_x); move(max_y-1,0); printw(_("Operation in progress - please wait")); refresh(); if (dev->ioa->sis64) { if (!remove_or_add_back_device_64bit(dev)) { clear(); move(max_y/2,0); printw(_(" Disk was not removed!") ); refresh(); sleep(5); } } else { ipr_write_dev_attr(dev, "delete", "1"); evaluate_device(dev, dev->ioa, 0); ipr_del_zeroed_dev(dev); } } } else if (action == IPR_VERIFY_CONC_ADD) { rc = process_conc_maint(i_con, IPR_WAIT_CONC_ADD); if (!rc) { getmaxyx(stdscr,max_y,max_x); move(max_y-1,0); printw(_("Operation in progress - please wait")); refresh(); if (ioa->sis64) wait_for_new_dev_64bit(ioa, res_path); else wait_for_new_dev(ioa, &res_addr); } } } return rc; } /** * format_in_prog - check to determine if format is in progress * @dev: ipr dev struct * * Returns: * 0 if format is not in progress / 1 if format is in progress **/ static int format_in_prog(struct ipr_dev *dev) { struct ipr_cmd_status cmd_status; struct sense_data_t sense_data; int rc; if (ipr_is_af_dasd_device(dev)) { rc = ipr_query_command_status(dev, &cmd_status); if (!rc && cmd_status.num_records != 0 && cmd_status.record->status == IPR_CMD_STATUS_IN_PROGRESS) return 1; } else if (ipr_is_gscsi(dev)) { rc = ipr_test_unit_ready(dev, &sense_data); if (rc == CHECK_CONDITION && (sense_data.error_code & 0x7F) == 0x70 && (sense_data.sense_key & 0x0F) == 0x02 && /* NOT READY */ sense_data.add_sense_code == 0x04 && /* LOGICAL UNIT NOT READY */ sense_data.add_sense_code_qual == 0x04) /* FORMAT IN PROGRESS */ return 1; } return 0; } /** * free_empty_slot - * @dev: ipr dev struct * * Returns: * nothing **/ static void free_empty_slot(struct ipr_dev *dev) { if (!dev->scsi_dev_data) return; if (dev->scsi_dev_data->type != IPR_TYPE_EMPTY_SLOT) return; free(dev->scsi_dev_data); dev->scsi_dev_data = NULL; free(dev); } /** * free_empty_slots - * @dev: ipr dev struct * @num: number of slots to free * * Returns: * nothing **/ static void free_empty_slots(struct ipr_dev **dev, int num) { int i; for (i = 0; i < num; i++) { if (!dev[i] || !dev[i]->scsi_dev_data) continue; if (dev[i]->scsi_dev_data->type != IPR_TYPE_EMPTY_SLOT) continue; free_empty_slot(dev[i]); dev[i] = NULL; } } /** * get_res_addrs - * @dev: ipr dev struct * * Returns: * 0 **/ static int get_res_addrs(struct ipr_dev *dev) { struct ipr_res_addr *ra; struct ipr_res_addr_aliases aliases; int i = 0; ipr_query_res_addr_aliases(dev->ioa, &(dev->res_addr[0]), &aliases); for_each_ra_alias(ra, &aliases) { memcpy(&(dev->res_addr[i]), ra, sizeof(*ra)); if (++i >= IPR_DEV_MAX_PATHS) break; } scsi_dbg(dev, "Resource address aliases: %08X %08X\n", IPR_GET_PHYSICAL_LOCATOR(&(dev->res_addr[0])), IPR_GET_PHYSICAL_LOCATOR(&(dev->res_addr[1]))); return 0; } /** * get_res_path - * @dev: ipr dev struct * * Returns: * 0 **/ static int get_res_path(struct ipr_dev *dev) { struct ipr_res_path *rp; struct ipr_res_path_aliases aliases; int i = 0; ipr_query_res_path_aliases(dev->ioa, &(dev->res_path[0]), &aliases); for_each_rp_alias(rp, &aliases) { memcpy(&(dev->res_path[i]), rp, sizeof(*rp)); if (++i >= IPR_DEV_MAX_PATHS) break; } while (i < IPR_DEV_MAX_PATHS) memset(&dev->res_path[i++], 0xff, sizeof(struct ipr_res_path)); return 0; } /** * alloc_empty_slot - * @ses: ipr dev struct * @slot: slot number * @is_vses: vses flag * * Returns: * ipr_dev struct **/ static struct ipr_dev *alloc_empty_slot(struct ipr_dev *ses, int slot, int is_vses, char *phy_loc) { struct ipr_ioa *ioa = ses->ioa; struct ipr_dev *dev; struct scsi_dev_data *scsi_dev_data; int bus = ses->scsi_dev_data->channel; int box; if (ioa->hop_count == IPR_2BIT_HOP) box = ses->scsi_dev_data->phy_2bit_hop.box; else box = ses->scsi_dev_data->phy.box; dev = calloc(1, sizeof(*dev)); scsi_dev_data = calloc(1, sizeof(*scsi_dev_data)); scsi_dev_data->type = IPR_TYPE_EMPTY_SLOT; scsi_dev_data->host = ioa->host_num; scsi_dev_data->lun = 0; if (is_vses) { scsi_dev_data->channel = slot; scsi_dev_data->id = 0; } else { scsi_dev_data->channel = bus; if (ioa->hop_count == IPR_2BIT_HOP) scsi_dev_data->id = slot | (box << 6); else scsi_dev_data->id = slot | (box << 5); } dev->res_addr[0].bus = scsi_dev_data->channel; dev->res_addr[0].target = scsi_dev_data->id; dev->res_addr[0].lun = 0; dev->scsi_dev_data = scsi_dev_data; dev->ses[0] = ses; dev->ioa = ioa; dev->physical_location[0] = '\0'; strncat(dev->physical_location, phy_loc, strlen(phy_loc)); get_res_addrs(dev); return dev; } /** * alloc_empty_slot_64bit - * @ses: ipr dev struct * @slot: slot number * @is_vses: vses flag * * Returns: * ipr_dev struct **/ static struct ipr_dev *alloc_empty_slot_64bit(struct ipr_dev *ses, int slot, int is_vses, char *phy_loc) { struct ipr_ioa *ioa = ses->ioa; struct ipr_dev *dev; struct scsi_dev_data *scsi_dev_data; char slot_buf[50]; int n, len; dev = calloc(1, sizeof(*dev)); scsi_dev_data = calloc(1, sizeof(*scsi_dev_data)); scsi_dev_data->type = IPR_TYPE_EMPTY_SLOT; scsi_dev_data->host = ioa->host_num; n = sprintf(slot_buf, "%02X",slot); strncpy(scsi_dev_data->res_path, ses->scsi_dev_data->res_path, strlen(ses->scsi_dev_data->res_path)); scsi_dev_data->res_path[strlen(ses->scsi_dev_data->res_path) -2] = slot_buf[0]; scsi_dev_data->res_path[strlen(ses->scsi_dev_data->res_path) -1] = slot_buf[1]; strncpy(dev->res_path_name, scsi_dev_data->res_path, strlen(scsi_dev_data->res_path)); len = strlen(ses->scsi_dev_data->res_path); memcpy(&dev->res_path[0], &ses->res_path[0], sizeof(struct ipr_res_path)); dev->res_path[0].res_path_bytes[len/3] = slot; dev->scsi_dev_data = scsi_dev_data; dev->ses[0] = ses; dev->ioa = ioa; dev->physical_location[0] = '\0'; strncat(dev->physical_location, phy_loc, strlen(phy_loc)); get_res_path(dev); return dev; } /** * can_perform_conc_action - * @dev: ipr dev struct * @action: action type * * Returns: * 1 if able to perform the action / 0 otherwise **/ static int can_perform_conc_action(struct ipr_dev *dev, int action) { if (dev->scsi_dev_data->type == TYPE_ROM || dev->scsi_dev_data->type==TYPE_TAPE) return 0; if (action == IPR_CONC_REMOVE) { if (format_in_prog(dev)) return 0; if (!ipr_can_remove_device(dev)) return 0; } else if (dev->scsi_dev_data && action != IPR_CONC_IDENTIFY) return 0; return 1; } /** * get_dev_for_slot - * @ses: ipr dev struct * @slot: slot number * @is_vses: action type * * Returns: * ipr_dev struct if success / NULL otherwise **/ static struct ipr_dev *get_dev_for_slot(struct ipr_dev *ses, int slot, int is_vses, char *phy_loc) { struct ipr_ioa *ioa = ses->ioa; struct ipr_dev *dev; struct ipr_res_addr *ra; int bus = ses->scsi_dev_data->channel; int box, i; if (ioa->hop_count == IPR_2BIT_HOP) box = ses->scsi_dev_data->phy_2bit_hop.box; else box = ses->scsi_dev_data->phy.box; for_each_dev(ses->ioa, dev) { if (ipr_is_ses(dev)) continue; for_each_ra(ra, dev) { if (is_vses && (ra->bus != slot || ra->target != 0)) continue; if (ioa->hop_count == IPR_2BIT_HOP) { if (!is_vses && (ra->bus != bus || ra->phy_2bit_hop.box != box || ra->phy_2bit_hop.phy != slot)) continue; } else { if (!is_vses && (ra->bus != bus || ra->phy.box != box || ra->phy.phy != slot)) continue; } for (i = 0; i < IPR_DEV_MAX_PATHS; i++) { if (dev->ses[i]) continue; dev->ses[i] = ses; } dev->physical_location[0] = '\0'; if (strlen(phy_loc)) strncat(dev->physical_location, phy_loc, strlen(phy_loc)); return dev; } } return NULL; } /** * get_dev_for_slot_64bit - * @ses: ipr dev struct * @slot: slot number * @is_vses: action type * * Returns: * ipr_dev struct if success / NULL otherwise **/ static struct ipr_dev *get_dev_for_slot_64bit(struct ipr_dev *ses, int slot, char *phy_loc) { struct ipr_dev *dev; struct ipr_res_path *rp; int i, dev_slot, res_path_len,ses_path_len; for_each_dev(ses->ioa, dev) { if (ipr_is_ses(dev)) continue; ses_path_len = strlen(ses->res_path_name); res_path_len = strlen(dev->res_path_name); dev_slot = strtoul(dev->res_path_name + (res_path_len - 2), NULL, 16); if (ses_path_len != res_path_len) continue; for_each_rp(rp, dev) { if ( !memcmp(&rp->res_path_bytes, &ses->res_path[0], ses_path_len/3) && slot == dev_slot ) { for (i = 0; i < IPR_DEV_MAX_PATHS; i++) { if (dev->ses[i]) continue; dev->ses[i] = ses; break; } dev->physical_location[0] = '\0'; if (strlen(phy_loc)) strncat(dev->physical_location, phy_loc, strlen(phy_loc)); return dev; } } } return NULL; } /** * search_empty_dev64 - * @dev: ipr dev struct * @devs: ipr dev struct array * @num_devs: number of devices * * Returns: * 1 if duplicate device detected / 0 otherwise **/ static int search_empty_dev64(struct ipr_dev *dev, struct ipr_dev **devs, int num_devs) { int i; u8 empty_res_path[8]; memset(&empty_res_path, 0xff, sizeof(struct ipr_res_path)); if (memcmp(&dev->res_path[1], &empty_res_path, sizeof(struct ipr_res_path))) return 0; for (i = 0; i < num_devs; i++) { if (devs[i]->scsi_dev_data && devs[i]->scsi_dev_data->type == IPR_TYPE_EMPTY_SLOT && !strncmp(dev->physical_location, devs[i]->physical_location, PHYSICAL_LOCATION_LENGTH)) { memcpy(&devs[i]->res_path[1], &dev->res_path[0], sizeof(struct ipr_res_path)); devs[i]->ses[1] = dev->ses[0]; return 1; } } return 0; } /** * conc_dev_is_dup - * @dev: ipr dev struct * @devs: ipr dev struct array * @num_devs: number of devices * * Returns: * 1 if duplicate device detected / 0 otherwise **/ static int conc_dev_is_dup(struct ipr_dev *dev, struct ipr_dev **devs, int num_devs) { int i; for (i = 0; i < num_devs; i++) if (dev == devs[i]) return 1; return 0; } /** * dev_is_dup - indicate whether or not the devices are the same * @first: ipr dev struct * @second: ipr dev struct * * Returns: * 1 if devices are the same / 0 otherwise **/ static int dev_is_dup(struct ipr_dev *first, struct ipr_dev *second) { struct ipr_res_addr *first_ra; struct ipr_res_addr *second_ra; if (first->ioa != second->ioa) return 0; for_each_ra(first_ra, first) { for_each_ra(second_ra, second) { if (!memcmp(first_ra, second_ra, sizeof(*first_ra))) return 1; } } return 0; } /** * dev_is_dup64 - indicate whether or not the devices are the same * @first: ipr dev struct * @second: ipr dev struct * * Returns: * 1 if devices are the same / 0 otherwise **/ static int dev_is_dup64(struct ipr_dev *first, struct ipr_dev *second) { struct ipr_res_path *first_rp; struct ipr_res_path *second_rp; struct ipr_res_path no_exist_rp; if (first->ioa != second->ioa) return 0; memset(&no_exist_rp, 0xff, sizeof(struct ipr_res_path)); for_each_rp(first_rp, first) { for_each_rp(second_rp, second) { if (!memcmp(first_rp, &no_exist_rp, sizeof(*first_rp)) || !memcmp(second_rp, &no_exist_rp, sizeof(*second_rp))) continue; if (!memcmp(first_rp, second_rp, sizeof(*first_rp))) return 1; } } return 0; } /** * find_dup - * @dev: ipr dev struct * @devs: ipr dev struct array * @num_devs: number of devices * * Returns: * ipr_dev struct if duplicate found / NULL otherwise **/ static struct ipr_dev *find_dup(struct ipr_dev *dev, struct ipr_dev **devs, int num_devs) { int i; for (i = 0; i < num_devs; i++) { if (dev->ioa->sis64) { if (dev_is_dup64(dev, devs[i])) return devs[i]; } else { if (dev_is_dup(dev, devs[i])) return devs[i]; } } return NULL; } /** * remove_dup_dev - * @dev: ipr dev struct * @devs: ipr dev struct array * @num_devs: number of devices * * Returns: * num_devs **/ static int remove_dup_dev(struct ipr_dev *dev, struct ipr_dev **devs, int num_devs) { int i, j; for (i = 0; i < num_devs; i++) { if (dev == devs[i]) { free_empty_slot(dev); devs[i] = NULL; for (j = (i + 1); j < num_devs; j++) { devs[j - 1] = devs[j]; devs[j] = NULL; } return (num_devs - 1); } } return num_devs; } /** * remove_conc_dups - * @devs: ipr dev struct array * @num_devs: number of devices * * Returns: * num_devs **/ static int remove_conc_dups(struct ipr_dev **devs, int num_devs) { int i; struct ipr_dev *dev; for (i = 0; i < num_devs; i++) { dev = find_dup(devs[i], &devs[i+1], (num_devs - i - 1)); if (!dev) continue; num_devs = remove_dup_dev(dev, devs, num_devs); } return num_devs; } /** * get_conc_devs - * @ret: ipr dev struct * @action: action typoe * * Returns: * num)devs **/ static int get_conc_devs(struct ipr_dev ***ret, int action) { struct ipr_ioa *ioa; struct ipr_encl_status_ctl_pg ses_data; struct ipr_drive_elem_status *elem_status, *overall; struct ipr_dev **devs = NULL; struct ipr_dev *ses, *dev; int num_devs = 0; int ses_bus, scsi_id_found, is_spi, is_vses; struct ipr_ses_config_pg ses_cfg; struct drive_elem_desc_pg drive_data; char phy_loc[PHYSICAL_LOCATION_LENGTH + 1]; int times, index; for_each_primary_ioa(ioa) { is_spi = ioa_is_spi(ioa); for_each_hotplug_dev(ioa, dev) { if (ioa->sis64) get_res_path(dev); else get_res_addrs(dev); } for_each_ses(ioa, ses) { times = 5; if (strlen(ses->physical_location) == 0) get_ses_phy_loc(ses); while (times--) { if (!ipr_receive_diagnostics(ses, 2, &ses_data, sizeof(ses_data))) break; } if (times < 0 ) continue; if (ipr_receive_diagnostics(ses, 1, &ses_cfg, sizeof(ses_cfg))) continue; if (ipr_receive_diagnostics(ses, 7, &drive_data, sizeof(drive_data))) continue; overall = ipr_get_overall_elem(&ses_data, &ses_cfg); ses_bus = ses->scsi_dev_data->channel; scsi_id_found = 0; if (!is_spi && (overall->device_environment == 0)) is_vses = 1; else is_vses = 0; scsi_dbg(ses, "%s\n", is_vses ? "Found VSES" : "Found real SES"); for_each_elem_status(elem_status, &ses_data, &ses_cfg) { index = index_in_page2(&ses_data, elem_status->slot_id); if (index != -1) get_drive_phy_loc_with_ses_phy_loc(ses, &drive_data, index, phy_loc, 0); if (elem_status->status == IPR_DRIVE_ELEM_STATUS_UNSUPP) continue; if (elem_status->status == IPR_DRIVE_ELEM_STATUS_NO_ACCESS) continue; if (is_spi && (scsi_id_found & (1 << elem_status->slot_id))) continue; scsi_id_found |= (1 << elem_status->slot_id); if (elem_status->status == IPR_DRIVE_ELEM_STATUS_EMPTY) { if (ioa->sis64) { dev = alloc_empty_slot_64bit(ses, elem_status->slot_id, is_vses, phy_loc); if (!search_empty_dev64(dev, devs, num_devs)){ devs = realloc(devs, (sizeof(struct ipr_dev *) * (num_devs + 1))); devs[num_devs++] = dev; } else free_empty_slot(dev); } else { devs = realloc(devs, (sizeof(struct ipr_dev *) * (num_devs + 1))); devs[num_devs++] = alloc_empty_slot(ses, elem_status->slot_id, is_vses, phy_loc); } continue; } if (ioa->sis64) dev = get_dev_for_slot_64bit(ses, elem_status->slot_id, phy_loc); else dev = get_dev_for_slot(ses, elem_status->slot_id, is_vses, phy_loc); if (dev && !can_perform_conc_action(dev, action)) continue; if (dev && conc_dev_is_dup(dev, devs, num_devs)) continue; else if (!dev) { if (ioa->sis64) dev = alloc_empty_slot_64bit(ses, elem_status->slot_id, is_vses, phy_loc); else dev = alloc_empty_slot(ses, elem_status->slot_id, is_vses, phy_loc); } devs = realloc(devs, (sizeof(struct ipr_dev *) * (num_devs + 1))); devs[num_devs++] = dev; } } } num_devs = remove_conc_dups(devs, num_devs); *ret = devs; return num_devs; } /** * start_conc_maint - * @i_con: i_container struct * @action: action type * * Returns: * 0 if success / non-zero on failure FIXME **/ int start_conc_maint(i_container *i_con, int action) { int rc, s_rc, i, k; char *buffer[3]; int num_lines = 0; struct screen_output *s_out; struct ipr_dev **devs; int toggle = 0; s_node *n_screen; int header_lines; int num_devs; processing(); rc = RC_SUCCESS; i_con = free_i_con(i_con); check_current_config(false); /* Setup screen title */ if (action == IPR_CONC_REMOVE) n_screen = &n_concurrent_remove_device; else n_screen = &n_concurrent_add_device; body_init_status_conc(buffer, n_screen->header, &header_lines); num_devs = get_conc_devs(&devs, action); for (i = 0; i < num_devs; i++) { print_dev_conc(k, devs[i], buffer, "%1", k); i_con = add_i_con(i_con,"\0", devs[i]); num_lines++; } if (num_lines == 0) { for (k = 0; k < 3; k++) buffer[k] = add_string_to_body(buffer[k], _("(No Eligible Devices Found)"), "", NULL); } do { n_screen->body = buffer[toggle%3]; s_out = screen_driver(n_screen, header_lines, i_con); s_rc = s_out->rc; free(s_out); toggle++; } while (s_rc == TOGGLE_SCREEN); for (k = 0; k < 3; k++) { free(buffer[k]); buffer[k] = NULL; } n_screen->body = NULL; if (!s_rc) { if (action == IPR_CONC_REMOVE) rc = process_conc_maint(i_con, IPR_VERIFY_CONC_REMOVE); else rc = process_conc_maint(i_con, IPR_VERIFY_CONC_ADD); } if (rc & CANCEL_FLAG) { s_status.index = (rc & ~CANCEL_FLAG); rc = (rc | REFRESH_FLAG) & ~CANCEL_FLAG; } free_empty_slots(devs, num_devs); free(devs); return rc; } /** * concurrent_add_device - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int concurrent_add_device(i_container *i_con) { return start_conc_maint(i_con, IPR_CONC_ADD); } /** * concurrent_remove_device - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int concurrent_remove_device(i_container *i_con) { return start_conc_maint(i_con, IPR_CONC_REMOVE); } /** * path_details - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int path_details(i_container *i_con) { int rc, header_lines = 2; char *body = NULL; struct ipr_dev *dev; struct screen_output *s_out; char location[512]; char *name; rc = RC_SUCCESS; processing(); if ((rc = device_details_get_device(i_con, &dev))) return rc; if (strlen(dev->dev_name)) name = dev->dev_name; else name = dev->gen_name; if (dev->ioa->sis64) sprintf(location, "%d/%s", dev->ioa->host_num,dev->scsi_dev_data->res_path); else sprintf(location, "%s/%d:%d:%d:%d", dev->ioa->pci_address, dev->ioa->host_num, dev->res_addr[0].bus, dev->res_addr[0].target, dev->res_addr[0].lun); body = add_line_to_body(body, _("Device"), name); if (strlen(dev->physical_location)) body = add_line_to_body(body, _("Location"), dev->physical_location); else body = add_line_to_body(body, _("Location"), location); body = __body_init(body, n_path_details.header, &header_lines); body = status_header(body, &header_lines, 6); body = print_path_details(dev, body); n_path_details.body = body; s_out = screen_driver(&n_path_details, header_lines, i_con); free(n_path_details.body); n_path_details.body = NULL; rc = s_out->rc; free(s_out); return rc; } /** * path_status - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int path_status(i_container * i_con) { int rc, k, header_lines; char *buffer[2]; struct ipr_ioa *ioa; struct ipr_dev *dev; struct screen_output *s_out; int toggle = 1, num_devs = 0; rc = RC_SUCCESS; processing(); i_con = free_i_con(i_con); check_current_config(false); for (k = 0; k < 2; k++) buffer[k] = __body_init_status(n_path_status.header, &header_lines, k); for_each_sas_ioa(ioa) { if (ioa->ioa_dead) continue; for_each_disk(ioa, dev) { buffer[0] = __print_device(dev, buffer[0], "%1", 1, 1, 1, 0, 0, 1, 0, 1, 0 ,0, 0, 0); buffer[1] = __print_device(dev, buffer[1], "%1", 1, 1, 0, 0, 0, 0, 0, 1, 0 ,0, 0, 0); i_con = add_i_con(i_con, "\0", dev); num_devs++; } } if (!num_devs) /* "No SAS disks available" */ return 76; do { n_path_status.body = buffer[toggle&1]; s_out = screen_driver(&n_path_status, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_path_status.body = NULL; return rc; } /** * init_device - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int init_device(i_container *i_con) { int rc, k; struct scsi_dev_data *scsi_dev_data; struct ipr_dev_record *device_record; int can_init; int dev_type; char *buffer[2]; int num_devs = 0; struct ipr_ioa *ioa; struct ipr_dev *dev; struct screen_output *s_out; int header_lines; int toggle = 0; rc = RC_SUCCESS; processing(); i_con = free_i_con(i_con); check_current_config(false); body_init_status(buffer, n_init_device.header, &header_lines); for_each_primary_ioa(ioa) { if (ioa->ioa_dead) continue; for_each_dev(ioa, dev) { can_init = 1; scsi_dev_data = dev->scsi_dev_data; /* If not a DASD, disallow format */ if (!scsi_dev_data || ipr_is_hot_spare(dev) || ipr_is_volume_set(dev) || !device_supported(dev) || (scsi_dev_data->type != TYPE_DISK && scsi_dev_data->type != IPR_TYPE_AF_DISK)) continue; /* If Advanced Function DASD */ if (ipr_is_af_dasd_device(dev)) { dev_type = IPR_AF_DASD_DEVICE; device_record = dev->dev_rcd; /* We allow the user to format the drive if nobody is using it */ if (scsi_dev_data->opens != 0) { syslog(LOG_ERR, _("Format not allowed to %s, device in use\n"), dev->gen_name); continue; } if (ipr_is_array_member(dev)) { if (device_record->no_cfgte_vol) can_init = 1; else can_init = 0; } else can_init = is_format_allowed(dev); } else if (scsi_dev_data->type == TYPE_DISK) { /* We allow the user to format the drive if nobody is using it */ if (scsi_dev_data->opens != 0) { syslog(LOG_ERR, _("Format not allowed to %s, device in use\n"), dev->gen_name); continue; } dev_type = IPR_JBOD_DASD_DEVICE; can_init = is_format_allowed(dev); } else continue; if (can_init) { add_format_device(dev, 0); print_dev(k, dev, buffer, "%1", k); i_con = add_i_con(i_con,"\0",dev_init_tail); num_devs++; } } } if (!num_devs) /* "No units available for initialize and format" */ return 49; do { n_init_device.body = buffer[toggle&1]; s_out = screen_driver(&n_init_device, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_init_device.body = NULL; free_devs_to_init(); return rc; } /** * confirm_init_device - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int confirm_init_device(i_container *i_con) { char *input; char *buffer[2]; struct devs_to_init_t *cur_dev_init; int rc = RC_SUCCESS; struct screen_output *s_out; int header_lines = 0; int toggle = 0; int k; i_container *temp_i_con; int found = 0; int post_attention = 0; struct ipr_dev *dev; for_each_icon(temp_i_con) { if (temp_i_con->data == NULL) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { found++; cur_dev_init =(struct devs_to_init_t *)(temp_i_con->data); cur_dev_init->do_init = 1; dev = cur_dev_init->dev; if (ipr_is_gscsi(dev)) post_attention++; } else { cur_dev_init = (struct devs_to_init_t *)(temp_i_con->data); cur_dev_init->do_init = 0; } } if (!found) return INVALID_OPTION_STATUS; if (post_attention) { for (k = 0; k < 2; k++) { header_lines = 0; buffer[k] = add_string_to_body(NULL, _("ATTENTION! System crash may occur " "if selected device is in use. Data loss will " "occur on selected device. Proceed with " "caution.\n\n"), "", &header_lines); buffer[k] = __body_init(buffer[k],n_confirm_init_device.header, &header_lines); buffer[k] = status_header(buffer[k], &header_lines, k); } } else { body_init_status(buffer, n_confirm_init_device.header, &header_lines); } for_each_dev_to_init(cur_dev_init) { if (!cur_dev_init->do_init) continue; print_dev(k, cur_dev_init->dev, buffer, "1", k); } do { n_confirm_init_device.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_init_device, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_init_device.body = NULL; return rc; } /** * dev_init_complete - * @num_devs: number of devices * * Returns: * 50 | EXIT_FLAG if success / 51 | EXIT_FLAG on failure **/ static int dev_init_complete(u8 num_devs) { int done_bad = 0; struct ipr_cmd_status cmd_status; struct ipr_cmd_status_record *status_record; int not_done = 0; int rc = 0; struct devs_to_init_t *dev; u32 percent_cmplt = 0; struct sense_data_t sense_data; struct ipr_ioa *ioa; int pid = 1; char dev_type[100]; int type; while(1) { if (pid) rc = complete_screen_driver(&n_dev_init_complete, percent_cmplt, 0); if (rc & EXIT_FLAG) { pid = fork(); if (pid) return rc; rc = 0; } percent_cmplt = 100; for_each_dev_to_init(dev) { if (!dev->do_init || dev->done) continue; if (ipr_read_dev_attr(dev->dev, "type", dev_type, 100)) { dev->done = 1; continue; } if (sscanf(dev_type, "%d", &type) != 1) { dev->done = 1; continue; } if (dev->dev_type == IPR_AF_DASD_DEVICE) { if (type != IPR_TYPE_AF_DISK) { dev->done = 1; continue; } rc = ipr_query_command_status(dev->dev, &cmd_status); if (rc || cmd_status.num_records == 0) { dev->done = 1; continue; } status_record = cmd_status.record; if (status_record->status == IPR_CMD_STATUS_FAILED) { done_bad = 1; dev->done = 1; } else if (status_record->status != IPR_CMD_STATUS_SUCCESSFUL) { if (status_record->percent_complete < percent_cmplt) percent_cmplt = status_record->percent_complete; not_done = 1; } else dev->done = 1; } else if (dev->dev_type == IPR_JBOD_DASD_DEVICE) { if (type != TYPE_DISK) { dev->done = 1; continue; } /* Send Test Unit Ready to find percent complete in sense data. */ rc = ipr_test_unit_ready(dev->dev, &sense_data); if (rc < 0) { dev->done = 1; done_bad = 1; continue; } if (rc == CHECK_CONDITION && (sense_data.error_code & 0x7F) == 0x70 && (sense_data.sense_key & 0x0F) == 0x02 && sense_data.add_sense_code == 0x04 && sense_data.add_sense_code_qual == 0x04) { dev->cmplt = ((int)sense_data.sense_key_spec[1]*100)/0x100; if (dev->cmplt < percent_cmplt) percent_cmplt = dev->cmplt; not_done = 1; } else dev->done = 1; } } if (!not_done) { for_each_dev_to_init(dev) { if (!dev->do_init) continue; ioa = dev->ioa; if (ipr_is_gscsi(dev->dev)) { rc = ipr_test_unit_ready(dev->dev, &sense_data); if (rc) { done_bad = 1; } else if (!ipr_is_af_blk_size(ioa, dev->new_block_size)) { ipr_write_dev_attr(dev->dev, "rescan", "1"); ipr_init_dev(dev->dev); } } if (dev->new_block_size != 0) { if (ipr_is_af_blk_size(ioa, dev->new_block_size)) enable_af(dev->dev); evaluate_device(dev->dev, ioa, dev->new_block_size); } if (ipr_is_af_blk_size(ioa, dev->new_block_size) || ipr_is_af_dasd_device(dev->dev)) ipr_add_zeroed_dev(dev->dev); } flush_stdscr(); if (done_bad) { if (!pid) exit(0); /* Initialize and format failed */ return 51 | EXIT_FLAG; } if (!pid) exit(0); /* Initialize and format completed successfully */ return 50 | EXIT_FLAG; } not_done = 0; sleep(1); } } /** * send_dev_inits - * @i_con: i_container struct * * Returns: * 50 | EXIT_FLAG if success / 51 | EXIT_FLAG on failure **/ int send_dev_inits(i_container *i_con) { u8 num_devs = 0; struct devs_to_init_t *cur_dev_init; int rc = 0; struct ipr_query_res_state res_state; u8 ioctl_buffer[IOCTL_BUFFER_SIZE]; struct ipr_mode_parm_hdr *mode_parm_hdr; struct ipr_block_desc *block_desc; struct scsi_dev_data *scsi_dev_data; struct ipr_ioa *ioa; int status; int opens; u8 failure = 0; int max_y, max_x; u8 length; if (use_curses) { getmaxyx(stdscr,max_y,max_x); mvaddstr(max_y-1,0,wait_for_next_screen); refresh(); } for_each_dev_to_init(cur_dev_init) { if (cur_dev_init->do_init && cur_dev_init->dev_type == IPR_AF_DASD_DEVICE) { num_devs++; ioa = cur_dev_init->ioa; rc = ipr_query_resource_state(cur_dev_init->dev, &res_state); if (rc != 0) { cur_dev_init->do_init = 0; num_devs--; failure++; continue; } scsi_dev_data = cur_dev_init->dev->scsi_dev_data; if (!scsi_dev_data) { cur_dev_init->do_init = 0; num_devs--; failure++; continue; } opens = num_device_opens(scsi_dev_data->host, scsi_dev_data->channel, scsi_dev_data->id, scsi_dev_data->lun); if ((opens != 0) && (!res_state.read_write_prot)) { syslog(LOG_ERR, _("Cannot obtain exclusive access to %s\n"), cur_dev_init->dev->gen_name); cur_dev_init->do_init = 0; num_devs--; failure++; continue; } ipr_disable_qerr(cur_dev_init->dev); if (cur_dev_init->new_block_size == IPR_JBOD_BLOCK_SIZE || cur_dev_init->new_block_size == IPR_JBOD_4K_BLOCK_SIZE) { /* Issue mode select to change block size */ mode_parm_hdr = (struct ipr_mode_parm_hdr *)ioctl_buffer; memset(ioctl_buffer, 0, 255); mode_parm_hdr->block_desc_len = sizeof(struct ipr_block_desc); block_desc = (struct ipr_block_desc *)(mode_parm_hdr + 1); /* Setup block size */ block_desc->block_length[0] = 0x00; block_desc->block_length[1] = cur_dev_init->new_block_size >> 8; block_desc->block_length[2] = cur_dev_init->new_block_size & 0xff; rc = ipr_mode_select(cur_dev_init->dev,ioctl_buffer, sizeof(struct ipr_block_desc) + sizeof(struct ipr_mode_parm_hdr)); if (rc != 0) { cur_dev_init->do_init = 0; num_devs--; failure++; continue; } } /* Issue the format. Failure will be detected by query command status */ rc = ipr_format_unit(cur_dev_init->dev); /* FIXME Mandatory lock? */ } else if (cur_dev_init->do_init && cur_dev_init->dev_type == IPR_JBOD_DASD_DEVICE) { num_devs++; ioa = cur_dev_init->ioa; scsi_dev_data = cur_dev_init->dev->scsi_dev_data; if (!scsi_dev_data) { cur_dev_init->do_init = 0; num_devs--; failure++; continue; } opens = num_device_opens(scsi_dev_data->host, scsi_dev_data->channel, scsi_dev_data->id, scsi_dev_data->lun); if (opens) { syslog(LOG_ERR, _("Cannot obtain exclusive access to %s\n"), cur_dev_init->dev->gen_name); cur_dev_init->do_init = 0; num_devs--; failure++; continue; } ipr_disable_qerr(cur_dev_init->dev); /* Issue mode select to setup block size */ mode_parm_hdr = (struct ipr_mode_parm_hdr *)ioctl_buffer; memset(ioctl_buffer, 0, 255); mode_parm_hdr->block_desc_len = sizeof(struct ipr_block_desc); block_desc = (struct ipr_block_desc *)(mode_parm_hdr + 1); /* xxx Setup block size */ if (cur_dev_init->new_block_size == ioa->af_block_size) { block_desc->block_length[0] = 0x00; block_desc->block_length[1] = ioa->af_block_size >> 8; block_desc->block_length[2] = ioa->af_block_size & 0xff; } else { block_desc->block_length[0] = 0x00; block_desc->block_length[1] = cur_dev_init->new_block_size >> 8; block_desc->block_length[2] = cur_dev_init->new_block_size & 0xff; } length = sizeof(struct ipr_block_desc) + sizeof(struct ipr_mode_parm_hdr); status = ipr_mode_select(cur_dev_init->dev, &ioctl_buffer, length); if (status && ipr_is_af_blk_size(ioa, cur_dev_init->new_block_size)) { cur_dev_init->do_init = 0; num_devs--; failure++; continue; } /* Issue format */ status = ipr_format_unit(cur_dev_init->dev); /* FIXME Mandatory lock? */ if (status) { /* Send a device reset to cleanup any old state */ rc = ipr_reset_device(cur_dev_init->dev); cur_dev_init->do_init = 0; num_devs--; failure++; continue; } } } if (num_devs) rc = dev_init_complete(num_devs); if (failure == 0) return rc; else /* "Initialize and format failed" */ return 51 | EXIT_FLAG; } /** * reclaim_cache - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int reclaim_cache(i_container* i_con) { int rc; struct ipr_ioa *ioa; struct ipr_reclaim_query_data *reclaim_buffer; struct ipr_reclaim_query_data *cur_reclaim_buffer; int found = 0; char *buffer[2]; struct screen_output *s_out; int header_lines; int toggle=1; int k; processing(); /* empty the linked list that contains field pointers */ i_con = free_i_con(i_con); check_current_config(false); body_init_status(buffer, n_reclaim_cache.header, &header_lines); reclaim_buffer = malloc(sizeof(struct ipr_reclaim_query_data) * num_ioas); for (ioa = ipr_ioa_head, cur_reclaim_buffer = reclaim_buffer; ioa != NULL; ioa = ioa->next, cur_reclaim_buffer++) { rc = ipr_reclaim_cache_store(ioa, IPR_RECLAIM_QUERY, cur_reclaim_buffer); if (rc != 0) { ioa->reclaim_data = NULL; continue; } ioa->reclaim_data = cur_reclaim_buffer; if (cur_reclaim_buffer->reclaim_known_needed || cur_reclaim_buffer->reclaim_unknown_needed) { print_dev(k, &ioa->ioa, buffer, "%1", k); i_con = add_i_con(i_con, "\0",ioa); found++; } } if (!found) { /* "No Reclaim IOA Cache Storage is necessary" */ rc = 38; goto leave; } do { n_reclaim_cache.body = buffer[toggle&1]; s_out = screen_driver(&n_reclaim_cache, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); leave: for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_reclaim_cache.body = NULL; return rc; } /** * confirm_reclaim - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int confirm_reclaim(i_container *i_con) { struct ipr_ioa *ioa, *reclaim_ioa; struct scsi_dev_data *scsi_dev_data; struct ipr_query_res_state res_state; struct ipr_dev *dev; int k, rc; char *buffer[2]; i_container* temp_i_con; struct screen_output *s_out; int header_lines; int toggle=1; int ioa_num = 0; for_each_icon(temp_i_con) { ioa = (struct ipr_ioa *) temp_i_con->data; if (!ioa) continue; if (strcmp(temp_i_con->field_data, "1") != 0) continue; if (ioa->reclaim_data->reclaim_known_needed || ioa->reclaim_data->reclaim_unknown_needed) { reclaim_ioa = ioa; ioa_num++; } } if (ioa_num == 0) /* "Invalid option. No devices selected." */ return 17; else if (ioa_num > 1) /* "Error. More than one unit was selected." */ return 16; /* One IOA selected, ready to proceed */ body_init_status(buffer, n_confirm_reclaim.header, &header_lines); for_each_dev(reclaim_ioa, dev) { scsi_dev_data = dev->scsi_dev_data; if (!scsi_dev_data || !ipr_is_af(dev)) continue; /* Do a query resource state to see whether or not the device will be affected by the operation */ rc = ipr_query_resource_state(dev, &res_state); if (rc != 0) res_state.not_oper = 1; if (!res_state.read_write_prot && !res_state.not_oper) continue; print_dev(k, dev, buffer, "1", k); } i_con = free_i_con(i_con); /* Save the chosen IOA for later use */ add_i_con(i_con, "\0", reclaim_ioa); do { n_confirm_reclaim.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_reclaim, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_reclaim.body = NULL; return rc; } /** * reclaim_warning - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int reclaim_warning(i_container *i_con) { struct ipr_ioa *reclaim_ioa; int k, rc; char *buffer[2]; struct screen_output *s_out; int header_lines; int toggle=1; reclaim_ioa = (struct ipr_ioa *)i_con->data; if (!reclaim_ioa) /* "Invalid option. No devices selected." */ return 17; for (k = 0; k < 2; k++) { buffer[k] = __body_init_status(n_confirm_reclaim_warning.header, &header_lines, k); buffer[k] = print_device(&reclaim_ioa->ioa, buffer[k], "1", k); } do { n_confirm_reclaim_warning.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_reclaim_warning, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_reclaim_warning.body = NULL; return rc; } /** * get_reclaim_results - * @buf: ipr_reclaim_query_data struct * * Returns: * ipr_reclaim_results struct **/ static char *get_reclaim_results(struct ipr_reclaim_query_data *buf) { char *body = NULL; char buffer[32]; if (buf->reclaim_known_performed) { body = add_string_to_body(body, _("IOA cache storage reclamation has completed. " "Use the number of lost sectors to decide whether " "to restore data from the most recent save media " "or to continue with possible data loss.\n"), "", NULL); if (buf->num_blocks_needs_multiplier) sprintf(buffer, "%12d", ntohs(buf->num_blocks) * IPR_RECLAIM_NUM_BLOCKS_MULTIPLIER); else sprintf(buffer, "%12d", ntohs(buf->num_blocks)); body = add_line_to_body(body, _("Number of lost sectors"), buffer); } else if (buf->reclaim_unknown_performed) { body = add_string_to_body(body, _("IOA cache storage reclamation has completed. " "The number of lost sectors could not be determined.\n"), "", NULL); } else { body = add_string_to_body(body, _("IOA cache storage reclamation has failed.\n"), "", NULL); } return body; } /** * print_reclaim_results - display reclaim results * @buf: ipr_reclaim_query_data struct * * Returns: * nothing **/ static void print_reclaim_results(struct ipr_reclaim_query_data *buf) { char *body = get_reclaim_results(buf); printf("%s", body); free(body); } /** * reclaim_result - * @i_con: i_container struct * * Returns: * EXIT_FLAG if success / 37 | EXIT_FLAG on failure **/ int reclaim_result(i_container *i_con) { int rc; struct ipr_ioa *reclaim_ioa; int max_y,max_x; struct screen_output *s_out; int action; reclaim_ioa = (struct ipr_ioa *) i_con->data; action = IPR_RECLAIM_PERFORM; /* Everything going according to plan. Proceed with reclaim. */ getmaxyx(stdscr,max_y,max_x); move(max_y-1,0); printw("Please wait - reclaim in progress"); refresh(); if (reclaim_ioa->reclaim_data->reclaim_unknown_needed) action |= IPR_RECLAIM_UNKNOWN_PERM; rc = ipr_reclaim_cache_store(reclaim_ioa, action, reclaim_ioa->reclaim_data); if (rc != 0) /* "Reclaim IOA Cache Storage failed" */ return (EXIT_FLAG | 37); memset(reclaim_ioa->reclaim_data, 0, sizeof(struct ipr_reclaim_query_data)); rc = ipr_reclaim_cache_store(reclaim_ioa, IPR_RECLAIM_QUERY, reclaim_ioa->reclaim_data); if (rc != 0) /* "Reclaim IOA Cache Storage failed" */ return (EXIT_FLAG | 37); n_reclaim_result.body = get_reclaim_results(reclaim_ioa->reclaim_data); s_out = screen_driver(&n_reclaim_result, 0, NULL); free(s_out); free(n_reclaim_result.body); n_reclaim_result.body = NULL; rc = ipr_reclaim_cache_store(reclaim_ioa, IPR_RECLAIM_RESET, reclaim_ioa->reclaim_data); if (rc != 0) rc = (EXIT_FLAG | 37); else rc = EXIT_FLAG; return rc; } /** * battery_maint - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int battery_maint(i_container *i_con) { int rc, k; struct ipr_reclaim_query_data *cur_reclaim_buffer; struct ipr_ioa *ioa; int found = 0; char *buffer[2]; struct screen_output *s_out; int header_lines; int toggle=1; i_con = free_i_con(i_con); body_init_status(buffer, n_battery_maint.header, &header_lines); cur_reclaim_buffer = malloc(sizeof(struct ipr_reclaim_query_data) * num_ioas); for (ioa = ipr_ioa_head; ioa; ioa = ioa->next, cur_reclaim_buffer++) { rc = ipr_reclaim_cache_store(ioa, IPR_RECLAIM_QUERY | IPR_RECLAIM_EXTENDED_INFO, cur_reclaim_buffer); if (rc != 0) { ioa->reclaim_data = NULL; continue; } ioa->reclaim_data = cur_reclaim_buffer; if (cur_reclaim_buffer->rechargeable_battery_type) { i_con = add_i_con(i_con, "\0",ioa); print_dev(k, &ioa->ioa, buffer, "%1", k); found++; } } if (!found) { /* No configured resources contain a cache battery pack */ rc = 44; goto leave; } do { n_battery_maint.body = buffer[toggle&1]; s_out = screen_driver(&n_battery_maint, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); leave: for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_battery_maint.body = NULL; return rc; } /** * get_battery_info - * @ioa: ipr ioa struct * * Returns: * char * **/ static char *get_battery_info(struct ipr_ioa *ioa) { struct ipr_reclaim_query_data *reclaim_data = ioa->reclaim_data; char buffer[128]; struct ipr_ioa_vpd ioa_vpd; struct ipr_cfc_vpd cfc_vpd; char product_id[IPR_PROD_ID_LEN+1]; char serial_num[IPR_SERIAL_NUM_LEN+1]; char *body = NULL; memset(&ioa_vpd, 0, sizeof(ioa_vpd)); memset(&cfc_vpd, 0, sizeof(cfc_vpd)); ipr_inquiry(&ioa->ioa, IPR_STD_INQUIRY, &ioa_vpd, sizeof(ioa_vpd)); ipr_inquiry(&ioa->ioa, 1, &cfc_vpd, sizeof(cfc_vpd)); ipr_strncpy_0(product_id, (char *)ioa_vpd.std_inq_data.vpids.product_id, IPR_PROD_ID_LEN); ipr_strncpy_0(serial_num, (char *)cfc_vpd.serial_num, IPR_SERIAL_NUM_LEN); body = add_line_to_body(body,"", NULL); body = add_line_to_body(body,_("Resource Name"), ioa->ioa.gen_name); body = add_line_to_body(body,_("Serial Number"), serial_num); body = add_line_to_body(body,_("Type"), product_id); body = add_line_to_body(body,_("PCI Address"), ioa->pci_address); if (ioa->sis64) body = add_line_to_body(body,_("Resource Path"), ioa->ioa.scsi_dev_data->res_path); sprintf(buffer,"%d", ioa->host_num); body = add_line_to_body(body,_("SCSI Host Number"), buffer); switch (reclaim_data->rechargeable_battery_type) { case IPR_BATTERY_TYPE_NICD: sprintf(buffer,_("Nickel Cadmium (NiCd)")); break; case IPR_BATTERY_TYPE_NIMH: sprintf(buffer,_("Nickel Metal Hydride (NiMH)")); break; case IPR_BATTERY_TYPE_LIION: sprintf(buffer,_("Lithium Ion (LiIon)")); break; default: sprintf(buffer,_("Unknown")); break; } body = add_line_to_body(body,_("Battery type"), buffer); switch (reclaim_data->rechargeable_battery_error_state) { case IPR_BATTERY_NO_ERROR_STATE: sprintf(buffer,_("No battery warning")); break; case IPR_BATTERY_WARNING_STATE: sprintf(buffer,_("Battery warning issued")); break; case IPR_BATTERY_ERROR_STATE: sprintf(buffer,_("Battery error issued")); break; default: sprintf(buffer,_("Unknown")); break; } body = add_line_to_body(body,_("Battery state"), buffer); sprintf(buffer,"%d",htons(reclaim_data->raw_power_on_time)); body = add_line_to_body(body,_("Power on time (days)"), buffer); sprintf(buffer,"%d",htons(reclaim_data->adjusted_power_on_time)); body = add_line_to_body(body,_("Adjusted power on time (days)"), buffer); sprintf(buffer,"%d",htons(reclaim_data->estimated_time_to_battery_warning)); body = add_line_to_body(body,_("Estimated time to warning (days)"), buffer); sprintf(buffer,"%d",htons(reclaim_data->estimated_time_to_battery_failure)); body = add_line_to_body(body,_("Estimated time to error (days)"), buffer); if (reclaim_data->conc_maint_battery) sprintf(buffer, "%s", _("Yes")); else sprintf(buffer, "%s", _("No")); body = add_line_to_body(body,_("Concurrently maintainable battery pack"), buffer); if (reclaim_data->battery_replace_allowed) sprintf(buffer, "%s", _("Yes")); else sprintf(buffer, "%s", _("No")); body = add_line_to_body(body,_("Battery pack can be safely replaced"), buffer); return body; } /** * show_battery_info - * @ioa: ipr ioa struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int show_battery_info(struct ipr_ioa *ioa) { struct screen_output *s_out; int rc; n_show_battery_info.body = get_battery_info(ioa); s_out = screen_driver(&n_show_battery_info, 0, NULL); free(n_show_battery_info.body); n_show_battery_info.body = NULL; rc = s_out->rc; free(s_out); return rc; } /** * confirm_force_battery_error - * * Returns: * 0 if success / non-zero on failure FIXME **/ int confirm_force_battery_error(void) { int rc,k; struct ipr_ioa *ioa; char *buffer[2]; struct screen_output *s_out; int header_lines; int toggle=1; rc = RC_SUCCESS; body_init_status(buffer, n_confirm_force_battery_error.header, &header_lines); for_each_ioa(ioa) { if (!ioa->reclaim_data || !ioa->ioa.is_reclaim_cand) continue; print_dev(k, &ioa->ioa, buffer, "2", k); } do { n_confirm_force_battery_error.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_force_battery_error, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_force_battery_error.body = NULL; return rc; } /** * enable_battery - * @i_con: i_container struct * * Returns: * 71 | EXIT_FLAG if success / 70 | EXIT_FLAG on failure **/ int enable_battery(i_container *i_con) { int rc, reclaim_rc; struct ipr_ioa *ioa; reclaim_rc = rc = RC_SUCCESS; for_each_ioa(ioa) { if (!ioa->reclaim_data || !ioa->ioa.is_reclaim_cand) continue; rc = ipr_reclaim_cache_store(ioa, IPR_RECLAIM_RESET_BATTERY_ERROR | IPR_RECLAIM_EXTENDED_INFO, ioa->reclaim_data); if (rc != 0) reclaim_rc = 70; if (ioa->reclaim_data->action_status != IPR_ACTION_SUCCESSFUL) { ioa_err(ioa, "Start IOA cache failed.\n"); reclaim_rc = 70; } } if (reclaim_rc != RC_SUCCESS) return reclaim_rc | EXIT_FLAG; return 71 | EXIT_FLAG; } /** * confirm_enable_battery - * * Returns: * 0 if success / non-zero on failure FIXME **/ int confirm_enable_battery(void) { int rc,k; struct ipr_ioa *ioa; char *buffer[2]; struct screen_output *s_out; int header_lines; int toggle=1; rc = RC_SUCCESS; body_init_status(buffer, n_confirm_start_cache.header, &header_lines); for_each_ioa(ioa) { if (!ioa->reclaim_data || !ioa->ioa.is_reclaim_cand) continue; print_dev(k, &ioa->ioa, buffer, "3", k); } do { n_confirm_start_cache.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_start_cache, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_start_cache.body = NULL; return rc; } /** * battery_fork - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int battery_fork(i_container *i_con) { int rc = 0; int force_error = 0; struct ipr_ioa *ioa; char *input; i_container *temp_i_con; for_each_icon(temp_i_con) { ioa = (struct ipr_ioa *)temp_i_con->data; if (ioa == NULL) continue; input = temp_i_con->field_data; if (strcmp(input, "3") == 0) { ioa->ioa.is_reclaim_cand = 1; return confirm_enable_battery(); } else if (strcmp(input, "2") == 0) { force_error++; ioa->ioa.is_reclaim_cand = 1; } else if (strcmp(input, "1") == 0) { rc = show_battery_info(ioa); return rc; } else ioa->ioa.is_reclaim_cand = 0; } if (force_error) { rc = confirm_force_battery_error(); return rc; } return INVALID_OPTION_STATUS; } /** * force_battery_error - * @i_con: i_container struct * * Returns: * 72 | EXIT_FLAG if success / 43 | EXIT_FLAG on failure **/ int force_battery_error(i_container *i_con) { int rc, reclaim_rc; struct ipr_ioa *ioa; struct ipr_reclaim_query_data reclaim_buffer; reclaim_rc = rc = RC_SUCCESS; for_each_ioa(ioa) { if (!ioa->reclaim_data || !ioa->ioa.is_reclaim_cand) continue; rc = ipr_reclaim_cache_store(ioa, IPR_RECLAIM_FORCE_BATTERY_ERROR, &reclaim_buffer); if (rc != 0) reclaim_rc = 43; if (reclaim_buffer.action_status != IPR_ACTION_SUCCESSFUL) { ioa_err(ioa, "Battery Force Error failed.\n"); reclaim_rc = 43; } } if (reclaim_rc != RC_SUCCESS) return reclaim_rc | EXIT_FLAG; return 72 | EXIT_FLAG; } /** * bus_config - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int bus_config(i_container *i_con) { int rc, k; int found = 0; char *buffer[2]; struct ipr_ioa *ioa; u8 ioctl_buffer[255]; struct ipr_mode_parm_hdr *mode_parm_hdr; struct ipr_mode_page_28_header *modepage_28_hdr; struct screen_output *s_out; int header_lines; int toggle = 0; rc = RC_SUCCESS; processing(); i_con = free_i_con(i_con); check_current_config(false); body_init_status(buffer, n_bus_config.header, &header_lines); for_each_ioa(ioa) { if ((!ioa_is_spi(ioa) || ioa->is_aux_cache) && !ipr_debug) continue; /* mode sense page28 to focal point */ mode_parm_hdr = (struct ipr_mode_parm_hdr *)ioctl_buffer; rc = ipr_mode_sense(&ioa->ioa, 0x28, mode_parm_hdr); if (rc != 0) continue; modepage_28_hdr = (struct ipr_mode_page_28_header *) (mode_parm_hdr + 1); if (modepage_28_hdr->num_dev_entries == 0) continue; i_con = add_i_con(i_con,"\0",(char *)ioa); print_dev(k, &ioa->ioa, buffer, "%1", k); found++; } if (!found) { n_bus_config_fail.body = body_init(n_bus_config_fail.header, NULL); s_out = screen_driver(&n_bus_config_fail, 0, NULL); free(n_bus_config_fail.body); n_bus_config_fail.body = NULL; rc = s_out->rc; free(s_out); } else { do { n_bus_config.body = buffer[toggle&1]; s_out = screen_driver(&n_bus_config, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); } for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_bus_config.body = NULL; return rc; } enum attr_type {qas_capability_type, scsi_id_type, bus_width_type, max_xfer_rate_type}; struct bus_attr { enum attr_type type; u8 bus; struct ipr_scsi_buses *page_28; struct ipr_ioa *ioa; struct bus_attr *next; }; struct bus_attr *bus_attr_head = NULL; /** * get_bus_attr_buf - Start array protection for an array * @bus_attr: bus_attr struct * * Returns: * bus_attr struct **/ static struct bus_attr *get_bus_attr_buf(struct bus_attr *bus_attr) { if (!bus_attr) { bus_attr_head = malloc(sizeof(struct bus_attr)); bus_attr_head->next = NULL; return bus_attr_head; } bus_attr->next = malloc(sizeof(struct bus_attr)); bus_attr->next->next = NULL; return bus_attr->next; } /** * free_all_bus_attr_buf - * * Returns: * nothing **/ static void free_all_bus_attr_buf(void) { struct bus_attr *bus_attr; while (bus_attr_head) { bus_attr = bus_attr_head->next; free(bus_attr_head); bus_attr_head = bus_attr; } } /** * get_changeable_bus_attr - * @ioa: ipr ioa struct * @page28: ipr_scsi_buses struct * @num_buses: number of busses * * Returns: * nothing **/ static void get_changeable_bus_attr(struct ipr_ioa *ioa, struct ipr_scsi_buses *page28, int num_buses) { int j; struct ipr_dev *dev; for (j = 0; j < num_buses; j++) { page28->bus[j].qas_capability = 0; if (ioa->scsi_id_changeable) page28->bus[j].scsi_id = 1; else page28->bus[j].scsi_id = 0; page28->bus[j].bus_width = 1; for_each_dev(ioa, dev) { if (!dev->scsi_dev_data) continue; if (j != dev->scsi_dev_data->channel) continue; if (dev->scsi_dev_data->id <= 7) continue; page28->bus[j].bus_width = 0; break; } page28->bus[j].max_xfer_rate = 1; } if (ioa->protect_last_bus && num_buses) { page28->bus[num_buses-1].qas_capability = 0; page28->bus[num_buses-1].scsi_id = 0; page28->bus[num_buses-1].bus_width = 0; page28->bus[num_buses-1].max_xfer_rate = 0; } } /** * change_bus_attr - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int change_bus_attr(i_container *i_con) { struct ipr_ioa *ioa; struct ipr_dev *dev; int rc, j; struct ipr_scsi_buses page_28_cur; struct ipr_scsi_buses page_28_chg; struct scsi_dev_data *scsi_dev_data; char scsi_id_str[5][16]; char max_xfer_rate_str[5][16]; i_container *temp_i_con; int found = 0; char *input; struct bus_attr *bus_attr = NULL; int header_lines = 0; char *body = NULL; char buffer[128]; struct screen_output *s_out; u8 bus_num; int bus_attr_menu(struct ipr_ioa *ioa, struct bus_attr *bus_attr, int row, int header_lines); rc = RC_SUCCESS; for_each_icon(temp_i_con) { ioa = (struct ipr_ioa *)temp_i_con->data; if (!ioa) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { found++; break; } } if (!found) return INVALID_OPTION_STATUS; i_con = free_i_con(i_con); /* zero out user page 28 page, this data is used to indicate which fields the user changed */ memset(&page_28_cur, 0, sizeof(struct ipr_scsi_buses)); memset(&page_28_chg, 0, sizeof(struct ipr_scsi_buses)); /* mode sense page28 to get current parms */ rc = ipr_get_bus_attr(ioa, &page_28_cur); if (rc != 0) /* "Change SCSI Bus configurations failed" */ return 46 | EXIT_FLAG; get_changeable_bus_attr(ioa, &page_28_chg, page_28_cur.num_buses); body = body_init(n_change_bus_attr.header, &header_lines); sprintf(buffer, "Adapter Location: %s\n", ioa->pci_address); body = add_line_to_body(body, buffer, NULL); header_lines++; for (j = 0; j < page_28_cur.num_buses; j++) { if (ioa->protect_last_bus && j == (page_28_cur.num_buses - 1)) continue; sprintf(buffer,_(" BUS%d"), j); body = add_line_to_body(body, buffer, NULL); if (page_28_chg.bus[j].qas_capability) { body = add_line_to_body(body,_("QAS Capability"), "%9"); bus_attr = get_bus_attr_buf(bus_attr); bus_attr->type = qas_capability_type; bus_attr->bus = j; bus_attr->page_28 = &page_28_cur; bus_attr->ioa = ioa; if (page_28_cur.bus[j].qas_capability == IPR_MODEPAGE28_QAS_CAPABILITY_DISABLE_ALL) i_con = add_i_con(i_con,_("Disabled"),bus_attr); else i_con = add_i_con(i_con,_("Enabled"),bus_attr); } if (page_28_chg.bus[j].scsi_id) { body = add_line_to_body(body,_("Host SCSI ID"), "%3"); bus_attr = get_bus_attr_buf(bus_attr); bus_attr->type = scsi_id_type; bus_attr->bus = j; bus_attr->page_28 = &page_28_cur; bus_attr->ioa = ioa; sprintf(scsi_id_str[j],"%d",page_28_cur.bus[j].scsi_id); i_con = add_i_con(i_con,scsi_id_str[j],bus_attr); } if (page_28_chg.bus[j].bus_width) { /* check if 8 bit bus is allowable with current configuration before enabling option */ for_each_dev(ioa, dev) { scsi_dev_data = dev->scsi_dev_data; if (scsi_dev_data && (scsi_dev_data->id & 0xF8) && (j == scsi_dev_data->channel)) { page_28_chg.bus[j].bus_width = 0; break; } } } if (page_28_chg.bus[j].bus_width) { body = add_line_to_body(body,_("Wide Enabled"), "%4"); bus_attr = get_bus_attr_buf(bus_attr); bus_attr->type = bus_width_type; bus_attr->bus = j; bus_attr->page_28 = &page_28_cur; bus_attr->ioa = ioa; if (page_28_cur.bus[j].bus_width == 16) i_con = add_i_con(i_con,_("Yes"),bus_attr); else i_con = add_i_con(i_con,_("No"),bus_attr); } if (page_28_chg.bus[j].max_xfer_rate) { body = add_line_to_body(body,_("Maximum Bus Throughput"), "%9"); bus_attr = get_bus_attr_buf(bus_attr); bus_attr->type = max_xfer_rate_type; bus_attr->bus = j; bus_attr->page_28 = &page_28_cur; bus_attr->ioa = ioa; sprintf(max_xfer_rate_str[j],"%d MB/s", (page_28_cur.bus[j].max_xfer_rate * page_28_cur.bus[j].bus_width)/(10 * 8)); i_con = add_i_con(i_con,max_xfer_rate_str[j],bus_attr); } } n_change_bus_attr.body = body; while (1) { s_out = screen_driver(&n_change_bus_attr, header_lines, i_con); rc = s_out->rc; free(s_out); for_each_icon(temp_i_con) { if (!temp_i_con->field_data[0]) { bus_attr = (struct bus_attr *)temp_i_con->data; rc = bus_attr_menu(ioa, bus_attr, temp_i_con->y, header_lines); if (rc == CANCEL_FLAG) rc = RC_SUCCESS; bus_num = bus_attr->bus; if (bus_attr->type == qas_capability_type) { if (page_28_cur.bus[bus_num].qas_capability == IPR_MODEPAGE28_QAS_CAPABILITY_DISABLE_ALL) sprintf(temp_i_con->field_data,_("Disable")); else sprintf(temp_i_con->field_data,_("Enable")); } else if (bus_attr->type == scsi_id_type) { sprintf(temp_i_con->field_data,"%d",page_28_cur.bus[bus_num].scsi_id); } else if (bus_attr->type == bus_width_type) { if (page_28_cur.bus[bus_num].bus_width == 16) sprintf(temp_i_con->field_data,_("Yes")); else sprintf(temp_i_con->field_data,_("No")); } else if (bus_attr->type == max_xfer_rate_type) { sprintf(temp_i_con->field_data,"%d MB/s", (page_28_cur.bus[bus_num].max_xfer_rate * page_28_cur.bus[bus_num].bus_width)/(10 * 8)); } break; } } if (rc) goto leave; } leave: free_all_bus_attr_buf(); free(n_change_bus_attr.body); n_change_bus_attr.body = NULL; free_i_con(i_con); return rc; } /** * confirm_change_bus_attr - * @i_con: i_container struct * * Returns: * 0 if success / 45 | EXIT_FLAG on failure **/ int confirm_change_bus_attr(i_container *i_con) { struct ipr_ioa *ioa; int rc, j; struct ipr_scsi_buses *page_28_cur; struct ipr_scsi_buses page_28_chg; char scsi_id_str[5][16]; char max_xfer_rate_str[5][16]; int header_lines = 0; char *body = NULL; char buffer[128]; struct screen_output *s_out; rc = RC_SUCCESS; page_28_cur = bus_attr_head->page_28; ioa = bus_attr_head->ioa; get_changeable_bus_attr(ioa, &page_28_chg, page_28_cur->num_buses); body = body_init(n_confirm_change_bus_attr.header, &header_lines); body = add_line_to_body(body, ioa->ioa.gen_name, NULL); header_lines++; for (j = 0; j < page_28_cur->num_buses; j++) { if (ioa->protect_last_bus && j == (page_28_cur->num_buses - 1)) continue; sprintf(buffer,_(" BUS%d"), j); body = add_line_to_body(body, buffer, NULL); if (page_28_chg.bus[j].qas_capability) { if (page_28_cur->bus[j].qas_capability == IPR_MODEPAGE28_QAS_CAPABILITY_DISABLE_ALL) body = add_line_to_body(body, _("QAS Capability"), _("Disable")); else body = add_line_to_body(body, _("QAS Capability"), _("Enable")); } if (page_28_chg.bus[j].scsi_id) { sprintf(scsi_id_str[j],"%d",page_28_cur->bus[j].scsi_id); body = add_line_to_body(body,_("Host SCSI ID"), scsi_id_str[j]); } if (page_28_chg.bus[j].bus_width) { if (page_28_cur->bus[j].bus_width == 16) body = add_line_to_body(body,_("Wide Enabled"), _("Yes")); else body = add_line_to_body(body,_("Wide Enabled"), _("No")); } if (page_28_chg.bus[j].max_xfer_rate) { sprintf(max_xfer_rate_str[j],"%d MB/s", (page_28_cur->bus[j].max_xfer_rate * page_28_cur->bus[j].bus_width)/(10 * 8)); body = add_line_to_body(body,_("Maximum Bus Throughput"), max_xfer_rate_str[j]); } } n_confirm_change_bus_attr.body = body; s_out = screen_driver(&n_confirm_change_bus_attr, header_lines, NULL); rc = s_out->rc; free(s_out); free(n_confirm_change_bus_attr.body); n_confirm_change_bus_attr.body = NULL; if (rc) return rc; processing(); rc = ipr_set_bus_attr(ioa, page_28_cur, 1); if (!rc) rc = 45 | EXIT_FLAG; return rc; } /** * bus_attr_menu - * @ioa: ipr ioa struct * @bus_attr: bus_attr struct * @start_row: starting row number * @header_lines: number of header lines * * Returns: * 0 if success / non-zero on failure **/ int bus_attr_menu(struct ipr_ioa *ioa, struct bus_attr *bus_attr, int start_row, int header_lines) { int i, scsi_id, found; int num_menu_items; int menu_index = 0; ITEM **menu_item = NULL; struct ipr_scsi_buses *page_28_cur; struct ipr_dev *dev; int *userptr; int *retptr; u8 bus_num; int max_bus_width, max_xfer_rate; int rc = RC_SUCCESS; /* start_row represents the row in which the top most menu item will be placed. Ideally this will be on the same line as the field in which the menu is opened for*/ start_row += 2; /* for title */ /* FIXME */ page_28_cur = bus_attr->page_28; bus_num = bus_attr->bus; if (bus_attr->type == qas_capability_type) { num_menu_items = 2; menu_item = malloc(sizeof(ITEM **) * (num_menu_items + 1)); userptr = malloc(sizeof(int) * num_menu_items); menu_item[0] = new_item("Enable",""); userptr[0] = 2; set_item_userptr(menu_item[0], (char *)&userptr[0]); menu_item[1] = new_item("Disable",""); userptr[1] = 1; set_item_userptr(menu_item[1], (char *)&userptr[1]); menu_item[2] = (ITEM *)NULL; rc = display_menu(menu_item, start_row, menu_index, &retptr); if ((rc == RC_SUCCESS) && (page_28_cur->bus[bus_num].qas_capability != *retptr)) { page_28_cur->bus[bus_num].qas_capability = *retptr; } i=0; while (menu_item[i] != NULL) free_item(menu_item[i++]); free(menu_item); free(userptr); menu_item = NULL; } else if (bus_attr->type == scsi_id_type) { num_menu_items = 16; menu_item = malloc(sizeof(ITEM **) * (num_menu_items + 1)); userptr = malloc(sizeof(int) * num_menu_items); menu_index = 0; for (scsi_id = 0, found = 0; scsi_id < 16; scsi_id++, found = 0) { for_each_dev(ioa, dev) { if (!dev->scsi_dev_data) continue; if (scsi_id == dev->scsi_dev_data->id && bus_num == dev->scsi_dev_data->channel) { found = 1; break; } } if (!found) { switch (scsi_id) { case 0: menu_item[menu_index] = new_item("0",""); userptr[menu_index] = 0; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; case 1: menu_item[menu_index] = new_item("1",""); userptr[menu_index] = 1; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; case 2: menu_item[menu_index] = new_item("2",""); userptr[menu_index] = 2; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; case 3: menu_item[menu_index] = new_item("3",""); userptr[menu_index] = 3; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; case 4: menu_item[menu_index] = new_item("4",""); userptr[menu_index] = 4; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; case 5: menu_item[menu_index] = new_item("5",""); userptr[menu_index] = 5; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; case 6: menu_item[menu_index] = new_item("6",""); userptr[menu_index] = 6; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; case 7: menu_item[menu_index] = new_item("7",""); userptr[menu_index] = 7; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; default: break; } } } menu_item[menu_index] = (ITEM *)NULL; rc = display_menu(menu_item, start_row, menu_index, &retptr); if ((rc == RC_SUCCESS) && (page_28_cur->bus[bus_num].scsi_id != *retptr)) { page_28_cur->bus[bus_num].scsi_id = *retptr; } i=0; while (menu_item[i] != NULL) free_item(menu_item[i++]); free(menu_item); free(userptr); menu_item = NULL; } else if (bus_attr->type == bus_width_type) { num_menu_items = 2; menu_item = malloc(sizeof(ITEM **) * (num_menu_items + 1)); userptr = malloc(sizeof(int) * num_menu_items); menu_index = 0; menu_item[menu_index] = new_item("No",""); userptr[menu_index] = 8; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; max_bus_width = 16; if (max_bus_width == 16) { menu_item[menu_index] = new_item("Yes",""); userptr[menu_index] = 16; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; } menu_item[menu_index] = (ITEM *)NULL; rc = display_menu(menu_item, start_row, menu_index, &retptr); if ((rc == RC_SUCCESS) && (page_28_cur->bus[bus_num].bus_width != *retptr)) { page_28_cur->bus[bus_num].bus_width = *retptr; } i=0; while (menu_item[i] != NULL) free_item(menu_item[i++]); free(menu_item); free(userptr); menu_item = NULL; } else if (bus_attr->type == max_xfer_rate_type) { num_menu_items = 7; menu_item = malloc(sizeof(ITEM **) * (num_menu_items + 1)); userptr = malloc(sizeof(int) * num_menu_items); menu_index = 0; max_xfer_rate = get_max_bus_speed(ioa, bus_num); switch (max_xfer_rate) { case 320: menu_item[menu_index] = new_item("320 MB/s",""); userptr[menu_index] = 3200; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; case 160: menu_item[menu_index] = new_item("160 MB/s",""); userptr[menu_index] = 1600; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; case 80: menu_item[menu_index] = new_item(" 80 MB/s",""); userptr[menu_index] = 800; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; case 40: menu_item[menu_index] = new_item(" 40 MB/s",""); userptr[menu_index] = 400; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; case 20: menu_item[menu_index] = new_item(" 20 MB/s",""); userptr[menu_index] = 200; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; case 10: menu_item[menu_index] = new_item(" 10 MB/s",""); userptr[menu_index] = 100; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; case 5: menu_item[menu_index] = new_item(" 5 MB/s",""); userptr[menu_index] = 50; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; default: menu_item[menu_index] = new_item(" ",""); userptr[menu_index] = 0; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; break; } menu_item[menu_index] = (ITEM *)NULL; rc = display_menu(menu_item, start_row, menu_index, &retptr); if ((rc == RC_SUCCESS) && (page_28_cur->bus[bus_num].max_xfer_rate != ((*retptr) * 8)/page_28_cur->bus[bus_num].bus_width)) { page_28_cur->bus[bus_num].max_xfer_rate = ((*retptr) * 8)/page_28_cur->bus[bus_num].bus_width; } i=0; while (menu_item[i] != NULL) free_item(menu_item[i++]); free(menu_item); free(userptr); menu_item = NULL; } return rc; } /** * display_menu - * @menu_item: menu items * @menu_start_row: start row * @menu_index_max: max menu index * @userptrptr: * * Returns: * 0 if success / non-zero on failure FIXME **/ int display_menu(ITEM **menu_item, int menu_start_row, int menu_index_max, int **userptrptr) { ITEM *cur_item; MENU *menu; WINDOW *box1_win, *box2_win, *field_win; int menu_rows, menu_cols, menu_rows_disp; int menu_index; int use_box2 = 0; int cur_item_index; int ch, i; int rc = RC_SUCCESS; int max_y,max_x; getmaxyx(stdscr,max_y,max_x); menu = new_menu(menu_item); scale_menu(menu, &menu_rows, &menu_cols); menu_rows_disp = menu_rows; /* check if fits in current screen, + 1 is added for bottom border, +1 is added for buffer */ if ((menu_start_row + menu_rows + 1 + 1) > max_y) { menu_start_row = max_y - (menu_rows + 1 + 1); if (menu_start_row < (8 + 1)) { /* size is adjusted by 2 to allow for top and bottom border, add +1 for buffer */ menu_rows_disp = max_y - (8 + 2 + 1); menu_start_row = (8 + 1); } } if (menu_rows > menu_rows_disp) { set_menu_format(menu, menu_rows_disp, 1); use_box2 = 1; } set_menu_mark(menu, "*"); scale_menu(menu, &menu_rows, &menu_cols); box1_win = newwin(menu_rows + 2, menu_cols + 2, menu_start_row - 1, 54); field_win = newwin(menu_rows, menu_cols, menu_start_row, 55); set_menu_win(menu, field_win); post_menu(menu); box(box1_win,ACS_VLINE,ACS_HLINE); if (use_box2) { box2_win = newwin(menu_rows + 2, 3, menu_start_row - 1, 54 + menu_cols + 1); box(box2_win,ACS_VLINE,ACS_HLINE); /* initialize scroll bar */ for (i = 0; i < menu_rows_disp/2; i++) mvwaddstr(box2_win, i + 1, 1, "#"); /* put down arrows in */ for (i = menu_rows_disp; i > (menu_rows_disp - menu_rows_disp/2); i--) mvwaddstr(box2_win, i, 1, "v"); } wnoutrefresh(box1_win); menu_index = 0; while (1) { if (use_box2) wnoutrefresh(box2_win); wnoutrefresh(field_win); doupdate(); ch = getch(); if (IS_ENTER_KEY(ch)) { cur_item = current_item(menu); cur_item_index = item_index(cur_item); *userptrptr = item_userptr(menu_item[cur_item_index]); break; } else if ((ch == KEY_DOWN) || (ch == '\t')) { if (use_box2 && (menu_index < (menu_index_max - 1))) { menu_index++; if (menu_index == menu_rows_disp) { /* put up arrows in */ for (i = 0; i < menu_rows_disp/2; i++) mvwaddstr(box2_win, i + 1, 1, "^"); } if (menu_index == (menu_index_max - 1)) { /* remove down arrows */ for (i = menu_rows_disp; i > (menu_rows_disp - menu_rows_disp/2); i--) mvwaddstr(box2_win, i, 1, "#"); } } menu_driver(menu,REQ_DOWN_ITEM); } else if (ch == KEY_UP) { if (use_box2 && menu_index != 0) { menu_index--; if (menu_index == (menu_index_max - menu_rows_disp - 1)) { /* put down arrows in */ for (i = menu_rows_disp; i > (menu_rows_disp - menu_rows_disp/2); i--) mvwaddstr(box2_win, i, 1, "v"); } if (menu_index == 0) { /* remove up arrows */ for (i = 0; i < menu_rows_disp/2; i++) mvwaddstr(box2_win, i + 1, 1, "#"); } } menu_driver(menu,REQ_UP_ITEM); } else if (IS_EXIT_KEY(ch)) { rc = EXIT_FLAG; break; } else if (IS_CANCEL_KEY(ch)) { rc = CANCEL_FLAG; break; } } unpost_menu(menu); free_menu(menu); wclear(field_win); wrefresh(field_win); delwin(field_win); wclear(box1_win); wrefresh(box1_win); delwin(box1_win); if (use_box2) { wclear(box2_win); wrefresh(box2_win); delwin(box2_win); } return rc; } /** * driver_config - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int driver_config(i_container *i_con) { int rc, k; char *buffer[2]; struct ipr_ioa *ioa; struct screen_output *s_out; int header_lines; int toggle = 0; processing(); /* empty the linked list that contains field pointers */ i_con = free_i_con(i_con); rc = RC_SUCCESS; check_current_config(false); body_init_status(buffer, n_driver_config.header, &header_lines); for_each_ioa(ioa) { i_con = add_i_con(i_con,"\0",ioa); print_dev(k, &ioa->ioa, buffer, "%1", k); } do { n_driver_config.body = buffer[toggle&1]; s_out = screen_driver(&n_driver_config, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_driver_config.body = NULL; return rc; } /** * get_log_level - return the log level * @ioa: ipr ioa struct * * Returns: * log level value if success / 0 on failure **/ int get_log_level(struct ipr_ioa *ioa) { char value_str[100]; int value; if (ipr_read_host_attr(ioa, "log_level", value_str, 100) < 0) return 0; sscanf(value_str, "%d", &value); return value; } /** * set_log_level - set the log level * @ioa: ipr ioa struct * @log_level: log level string * * Returns: * nothing **/ void set_log_level(struct ipr_ioa *ioa, char *log_level) { ipr_write_host_attr(ioa, "log_level", log_level, strlen(log_level)); } /** * change_driver_config - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure **/ int change_driver_config(i_container *i_con) { int rc, i, digits; int found = 0; i_container *temp_i_con; struct ipr_ioa *ioa; struct screen_output *s_out; char buffer[128]; char *body = NULL; char *input; for_each_icon(temp_i_con) { ioa = (struct ipr_ioa *)(temp_i_con->data); if (ioa == NULL) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { found++; break; } } if (!found) return 2; i_con = free_i_con(i_con); /* i_con to return field data */ i_con = add_i_con(i_con,"",NULL); body = body_init(n_change_driver_config.header, NULL); sprintf(buffer,"Adapter Location: %s\n", ioa->pci_address); body = add_line_to_body(body, buffer, NULL); sprintf(buffer,"%d", get_log_level(ioa)); body = add_line_to_body(body,_("Current Log Level"), buffer); body = add_line_to_body(body,_("New Log Level"), "%2"); n_change_driver_config.body = body; s_out = screen_driver(&n_change_driver_config, 0, i_con); free(n_change_driver_config.body); n_change_driver_config.body = NULL; rc = s_out->rc; if (!rc) { for (i = 0, digits = 0; i < strlen(i_con->field_data); i++) { if (isdigit(i_con->field_data[i])) { digits++; } else if (!isspace(i_con->field_data[i])) { rc = 48; break; } } if (!rc && digits) { set_log_level(ioa, i_con->field_data); rc = 57; } } free(s_out); return rc; } /** * disk_config - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int disk_config(i_container * i_con) { int rc, k; int len = 0; int num_lines = 0; struct ipr_ioa *ioa; struct screen_output *s_out; int header_lines; char *buffer[2]; int toggle = 1; processing(); rc = RC_SUCCESS; i_con = free_i_con(i_con); check_current_config(false); body_init_status(buffer, n_disk_config.header, &header_lines); for_each_ioa(ioa) { if (ioa->is_secondary || ioa->ioa_dead || !ioa->ioa.scsi_dev_data) continue; num_lines += print_standalone_disks(ioa, &i_con, buffer, 0); num_lines += print_hotspare_disks(ioa, &i_con, buffer, 0); num_lines += print_vsets(ioa, &i_con, buffer, 0); } if (num_lines == 0) { for (k = 0; k < 2; k++) { len = strlen(buffer[k]); buffer[k] = realloc(buffer[k], len + strlen(_(no_dev_found)) + 8); sprintf(buffer[k] + len, "\n%s", _(no_dev_found)); } } do { n_disk_config.body = buffer[toggle&1]; s_out = screen_driver(&n_disk_config, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_disk_config.body = NULL; return rc; } struct disk_config_attr { int option; int queue_depth; int format_timeout; int tcq_enabled; }; /** * disk_config_menu - * @disk_config_attr: disk_config_attr struct * @start_row: start row number * @header_lines: number of header lines * * Returns: * 0 if success / non-zero on failure FIXME **/ static int disk_config_menu(struct disk_config_attr *disk_config_attr, int start_row, int header_lines) { int i; int num_menu_items; int menu_index; ITEM **menu_item = NULL; int *retptr; int *userptr = NULL; int rc = RC_SUCCESS; /* start_row represents the row in which the top most menu item will be placed. Ideally this will be on the same line as the field in which the menu is opened for*/ start_row += 2; /* for title */ /* FIXME */ if (disk_config_attr->option == 1) { /* queue depth*/ rc = display_input(start_row, &disk_config_attr->queue_depth); if (disk_config_attr->queue_depth > 128) disk_config_attr->queue_depth = 128; } else if (disk_config_attr->option == 3) { /* tag command queue.*/ num_menu_items = 2; menu_item = malloc(sizeof(ITEM **) * (num_menu_items + 1)); userptr = malloc(sizeof(int) * num_menu_items); menu_index = 0; menu_item[menu_index] = new_item("No",""); userptr[menu_index] = 0; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = new_item("Yes",""); userptr[menu_index] = 1; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = (ITEM *)NULL; rc = display_menu(menu_item, start_row, menu_index, &retptr); if (rc == RC_SUCCESS) disk_config_attr->tcq_enabled = *retptr; i=0; while (menu_item[i] != NULL) free_item(menu_item[i++]); free(menu_item); free(userptr); menu_item = NULL; } else if (disk_config_attr->option == 2) { /* format t.o.*/ num_menu_items = 5; menu_item = malloc(sizeof(ITEM **) * (num_menu_items + 1)); userptr = malloc(sizeof(int) * num_menu_items); menu_index = 0; menu_item[menu_index] = new_item("8 hr",""); userptr[menu_index] = 8; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = new_item("4 hr",""); userptr[menu_index] = 4; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = new_item("3 hr",""); userptr[menu_index] = 3; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = new_item("2 hr",""); userptr[menu_index] = 2; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = new_item("1 hr",""); userptr[menu_index] = 1; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = (ITEM *)NULL; rc = display_menu(menu_item, start_row, menu_index, &retptr); if (rc == RC_SUCCESS) disk_config_attr->format_timeout = *retptr; i=0; while (menu_item[i] != NULL) free_item(menu_item[i++]); free(menu_item); free(userptr); menu_item = NULL; } return rc; } /** * change_disk_config - * @i_con: i_container struct * * Returns: * non-zero **/ int change_disk_config(i_container * i_con) { int rc; i_container *i_con_head_saved; struct ipr_dev *dev; char qdepth_str[4]; char format_timeout_str[5]; char buffer[128]; i_container *temp_i_con; int found = 0; char *input; struct disk_config_attr disk_config_attr[3]; struct disk_config_attr *config_attr = NULL; struct ipr_disk_attr disk_attr; int header_lines = 0; char *body = NULL; struct screen_output *s_out; struct ipr_array_record *array_record; int tcq_warning = 0; int tcq_blocked = 0; int tcq_enabled = 0; rc = RC_SUCCESS; for_each_icon(temp_i_con) { dev = (struct ipr_dev *)temp_i_con->data; if (!dev) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { found++; break; } } if (!found) return INVALID_OPTION_STATUS; rc = ipr_get_dev_attr(dev, &disk_attr); if (rc) return 66 | EXIT_FLAG; i_con_head_saved = i_con_head; /* FIXME */ i_con_head = i_con = NULL; body = body_init(n_change_disk_config.header, &header_lines); sprintf(buffer, "Device: %s %s\n", dev->scsi_dev_data->sysfs_device_name, dev->dev_name); header_lines += 2; if (ipr_is_volume_set(dev) || page0x0a_setup(dev)) header_lines += 1; /* xxx else if (ipr_is_af_dasd_device(dev) && disk_attr.queue_depth > 1) tcq_warning = 1; else if (ipr_is_gscsi(dev) && disk_attr.tcq_enabled) tcq_warning = 1; else tcq_blocked = 1; */ if (tcq_warning) { body = add_line_to_body(body, buffer, NULL); body = add_string_to_body(body, _("Note: Tagged queuing to this device is ENABLED " "and it does not support the required " "control mode page settings to run safely in this mode!\n\n"), "", &header_lines); } else if (tcq_blocked) { body = add_line_to_body(body, buffer, NULL); body = add_string_to_body(body, _("Note: Tagged queuing to this device is disabled by default " "since it does not support the required control mode page " "settings to run safely in this mode.\n\n"), "", &header_lines); } if (ipr_force) tcq_warning = tcq_blocked = 0; if (!tcq_blocked || ipr_is_gscsi(dev)) { body = add_line_to_body(body,_("Queue Depth"), "%3"); disk_config_attr[0].option = 1; disk_config_attr[0].queue_depth = disk_attr.queue_depth; sprintf(qdepth_str,"%d",disk_config_attr[0].queue_depth); i_con = add_i_con(i_con, qdepth_str, &disk_config_attr[0]); } array_record = dev->array_rcd; if (array_record && ipr_is_vset_record(array_record->common.record_id)) { /* VSET, no further fields */ } else if (ipr_is_af_dasd_device(dev)) { disk_config_attr[1].option = 2; disk_config_attr[1].format_timeout = disk_attr.format_timeout / (60 * 60); body = add_line_to_body(body,_("Format Timeout"), "%4"); sprintf(format_timeout_str,"%d hr",disk_config_attr[1].format_timeout); i_con = add_i_con(i_con, format_timeout_str, &disk_config_attr[1]); } else if (!tcq_blocked) { disk_config_attr[2].option = 3; disk_config_attr[2].tcq_enabled = disk_attr.tcq_enabled; body = add_line_to_body(body,_("Tag Command Queueing"), "%4"); tcq_enabled = disk_config_attr[2].tcq_enabled; if (disk_config_attr[2].tcq_enabled) i_con = add_i_con(i_con,_("Yes"),&disk_config_attr[2]); else i_con = add_i_con(i_con,_("No"),&disk_config_attr[2]); } n_change_disk_config.body = body; while (1) { s_out = screen_driver(&n_change_disk_config, header_lines, i_con); rc = s_out->rc; free(s_out); found = 0; for_each_icon(temp_i_con) { if (!temp_i_con->field_data[0]) { config_attr = (struct disk_config_attr *)temp_i_con->data; rc = disk_config_menu(config_attr, temp_i_con->y, header_lines); if (rc == CANCEL_FLAG) rc = RC_SUCCESS; if (config_attr->option == 1) { sprintf(temp_i_con->field_data,"%d", config_attr->queue_depth); disk_attr.queue_depth = config_attr->queue_depth; } else if (config_attr->option == 2) { sprintf(temp_i_con->field_data,"%d hr", config_attr->format_timeout); disk_attr.format_timeout = config_attr->format_timeout * 60 * 60; } else if (config_attr->option == 3) { if (config_attr->tcq_enabled) sprintf(temp_i_con->field_data,_("Yes")); else sprintf(temp_i_con->field_data,_("No")); disk_attr.tcq_enabled = config_attr->tcq_enabled; } found++; break; } } if (rc) goto leave; if (!found) break; } ipr_set_dev_attr(dev, &disk_attr, 1); leave: free(n_change_disk_config.body); n_change_disk_config.body = NULL; free_i_con(i_con); i_con_head = i_con_head_saved; return rc; } struct ioa_config_attr { int option; int preferred_primary; int gscsi_only_ha; int active_active; int caching; }; /** * ioa_config_menu - * @ioa_config_attr: ipr_config_attr struct * @start_row: start row * @header_lines: number of header lines * * Returns: * 0 if success / non-zero on failure **/ int ioa_config_menu(struct ioa_config_attr *ioa_config_attr, int start_row, int header_lines) { int i; int num_menu_items; int menu_index; ITEM **menu_item = NULL; int *retptr; int *userptr = NULL; int rc = RC_SUCCESS; /* start_row represents the row in which the top most menu item will be placed. Ideally this will be on the same line as the field in which the menu is opened for*/ start_row += 2; /* for title */ /* FIXME */ if (ioa_config_attr->option == 1 || ioa_config_attr->option == 2 || ioa_config_attr->option == 3 || ioa_config_attr->option == 4) { num_menu_items = 4; menu_item = malloc(sizeof(ITEM **) * (num_menu_items + 1)); userptr = malloc(sizeof(int) * num_menu_items); menu_index = 0; if (ioa_config_attr->option == 1) menu_item[menu_index] = new_item("None",""); else if (ioa_config_attr->option == 2) menu_item[menu_index] = new_item("RAID",""); else if (ioa_config_attr->option == 3) menu_item[menu_index] = new_item("Disabled",""); else menu_item[menu_index] = new_item("Default",""); userptr[menu_index] = 0; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; if (ioa_config_attr->option == 1) menu_item[menu_index] = new_item("Primary",""); else if (ioa_config_attr->option == 2) menu_item[menu_index] = new_item("JBOD",""); else if (ioa_config_attr->option == 3) menu_item[menu_index] = new_item("Enabled",""); else menu_item[menu_index] = new_item("Disabled",""); userptr[menu_index] = 1; set_item_userptr(menu_item[menu_index], (char *)&userptr[menu_index]); menu_index++; menu_item[menu_index] = (ITEM *)NULL; rc = display_menu(menu_item, start_row, menu_index, &retptr); if (rc == RC_SUCCESS) { if (ioa_config_attr->option == 1) ioa_config_attr->preferred_primary = *retptr; else if (ioa_config_attr->option == 2) ioa_config_attr->gscsi_only_ha = *retptr; else if (ioa_config_attr->option == 3) ioa_config_attr->active_active = *retptr; else ioa_config_attr->caching = *retptr; } i = 0; while (menu_item[i] != NULL) free_item(menu_item[i++]); free(menu_item); free(userptr); menu_item = NULL; } return rc; } /** * change_ioa_config - * @i_con: i_container struct * * Returns: * non-zero on exit **/ int change_ioa_config(i_container * i_con) { int rc; i_container *i_con_head_saved; struct ipr_dev *dev; char pref_str[20]; char buffer[128]; i_container *temp_i_con; int found = 0; char *input; struct ioa_config_attr ioa_config_attr[3]; struct ioa_config_attr *config_attr = NULL; struct ipr_ioa_attr ioa_attr; int header_lines = 0, index = 0; char *body = NULL; struct screen_output *s_out; rc = RC_SUCCESS; for_each_icon(temp_i_con) { dev = (struct ipr_dev *)temp_i_con->data; if (!dev) continue; input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { found++; break; } } if (!found) return INVALID_OPTION_STATUS; rc = ipr_get_ioa_attr(dev->ioa, &ioa_attr); if (rc) return 66 | EXIT_FLAG; i_con_head_saved = i_con_head; /* FIXME */ i_con_head = i_con = NULL; body = body_init(n_change_ioa_config.header, &header_lines); sprintf(buffer, "Adapter: %s/%d %s %s\n", dev->ioa->pci_address, dev->ioa->host_num, dev->scsi_dev_data->vendor_id, dev->scsi_dev_data->product_id); body = add_line_to_body(body, buffer, NULL); header_lines += 2; if (dev->ioa->dual_raid_support) { body = add_line_to_body(body,_("Preferred Dual Adapter State"), "%13"); ioa_config_attr[index].option = 1; ioa_config_attr[index].preferred_primary = ioa_attr.preferred_primary; if (ioa_attr.preferred_primary) sprintf(pref_str, "Primary"); else sprintf(pref_str, "None"); i_con = add_i_con(i_con, pref_str, &ioa_config_attr[index++]); } if (dev->ioa->gscsi_only_ha) { body = add_line_to_body(body,_("High-Availability Mode"), "%13"); ioa_config_attr[index].option = 2; ioa_config_attr[index].gscsi_only_ha = ioa_attr.gscsi_only_ha; if (ioa_attr.gscsi_only_ha) sprintf(pref_str, "JBOD"); else sprintf(pref_str, "Normal"); i_con = add_i_con(i_con, pref_str, &ioa_config_attr[index++]); } if (dev->ioa->asymmetric_access) { body = add_line_to_body(body,_("Active/Active Mode"), "%13"); ioa_config_attr[index].option = 3; ioa_config_attr[index].active_active = ioa_attr.active_active; if (ioa_attr.active_active) sprintf(pref_str, "Enabled"); else sprintf(pref_str, "Disabled"); i_con = add_i_con(i_con, pref_str, &ioa_config_attr[index++]); } if (dev->ioa->has_cache) { body = add_line_to_body(body,_("IOA Caching Mode"), "%13"); ioa_config_attr[index].option = 4; ioa_config_attr[index].caching = ioa_attr.caching; if (ioa_attr.caching == IPR_IOA_REQUESTED_CACHING_DEFAULT) sprintf(pref_str, "Default"); else sprintf(pref_str, "Disabled"); i_con = add_i_con(i_con, pref_str, &ioa_config_attr[index++]); } n_change_ioa_config.body = body; while (1) { s_out = screen_driver(&n_change_ioa_config, header_lines, i_con); rc = s_out->rc; free(s_out); found = 0; for_each_icon(temp_i_con) { if (!temp_i_con->field_data[0]) { config_attr = (struct ioa_config_attr *)temp_i_con->data; rc = ioa_config_menu(config_attr, temp_i_con->y, header_lines); if (rc == CANCEL_FLAG) rc = RC_SUCCESS; if (config_attr->option == 1) { if (config_attr->preferred_primary) sprintf(temp_i_con->field_data, "Primary"); else sprintf(temp_i_con->field_data, "None"); ioa_attr.preferred_primary = config_attr->preferred_primary; } else if (config_attr->option == 2) { if (config_attr->gscsi_only_ha) sprintf(temp_i_con->field_data, "JBOD"); else sprintf(temp_i_con->field_data, "Normal"); ioa_attr.gscsi_only_ha = config_attr->gscsi_only_ha; } else if (config_attr->option == 3) { if (config_attr->active_active) sprintf(temp_i_con->field_data, "Enabled"); else sprintf(temp_i_con->field_data, "Disabled"); ioa_attr.active_active = config_attr->active_active; } else if (config_attr->option == 4) { if (config_attr->caching == IPR_IOA_REQUESTED_CACHING_DEFAULT) sprintf(temp_i_con->field_data, "Default"); else sprintf(temp_i_con->field_data, "Disabled"); ioa_attr.caching = config_attr->caching; } found++; break; } } if (rc) goto leave; if (!found) break; } processing(); rc = ipr_set_ioa_attr(dev->ioa, &ioa_attr, 1); check_current_config(false); leave: free(n_change_ioa_config.body); n_change_ioa_config.body = NULL; free_i_con(i_con); i_con_head = i_con_head_saved; return rc; } /** * ioa_config - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int ioa_config(i_container * i_con) { int rc, k; int len = 0; int num_lines = 0; struct ipr_ioa *ioa; struct screen_output *s_out; int header_lines; char *buffer[2]; int toggle = 1; processing(); rc = RC_SUCCESS; i_con = free_i_con(i_con); check_current_config(false); body_init_status(buffer, n_ioa_config.header, &header_lines); for_each_ioa(ioa) { if (ioa->ioa.scsi_dev_data == NULL) continue; if (!ioa->dual_raid_support && !ioa->gscsi_only_ha) continue; print_dev(k, &ioa->ioa, buffer, "%1", k); i_con = add_i_con(i_con, "\0", &ioa->ioa); num_lines++; } if (num_lines == 0) { for (k = 0; k < 2; k++) { len = strlen(buffer[k]); buffer[k] = realloc(buffer[k], len + strlen(_(no_dev_found)) + 8); sprintf(buffer[k] + len, "\n%s", _(no_dev_found)); } } do { n_ioa_config.body = buffer[toggle&1]; s_out = screen_driver(&n_ioa_config, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_ioa_config.body = NULL; return rc; } /** * download_ucode - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int download_ucode(i_container * i_con) { int rc, k; int len = 0; int num_lines = 0; struct ipr_ioa *ioa; struct ipr_dev *dev, *vset; struct screen_output *s_out; int header_lines; char *buffer[2]; int toggle = 0; processing(); rc = RC_SUCCESS; i_con = free_i_con(i_con); check_current_config(false); body_init_status(buffer, n_download_ucode.header, &header_lines); for_each_ioa(ioa) { if (!ioa->ioa.scsi_dev_data || ioa->ioa_dead) continue; print_dev(k, &ioa->ioa, buffer, "%1", k); i_con = add_i_con(i_con, "\0", &ioa->ioa); num_lines++; if (ioa->is_secondary) continue; num_lines += print_standalone_disks(ioa, &i_con, buffer, 0); num_lines += print_sas_ses_devices(ioa, &i_con, buffer, 0); num_lines += print_hotspare_disks(ioa, &i_con, buffer, 0); /* print volume set associated devices*/ for_each_vset(ioa, vset) { num_lines++; for_each_dev_in_vset(vset, dev) { print_dev(k, dev, buffer, "%1", k); i_con = add_i_con(i_con, "\0", dev); num_lines++; } } } if (num_lines == 0) { for (k = 0; k < 2; k++) { len = strlen(buffer[k]); buffer[k] = realloc(buffer[k], len + strlen(_(no_dev_found)) + 8); sprintf(buffer[k] + len, "\n%s", _(no_dev_found)); } } do { n_download_ucode.body = buffer[toggle&1]; s_out = screen_driver(&n_download_ucode, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_download_ucode.body = NULL; return rc; } /** * update_ucode - * @dev: ipr dev struct * @fw_image: ipr_fw_images struct * * Returns: * 0 if success / 67 | EXIT_FLAG on failure **/ static int update_ucode(struct ipr_dev *dev, struct ipr_fw_images *fw_image) { char *body; int header_lines, status, time = 0; pid_t pid, done; u8 release_level[5]; u32 fw_version; int rc = 0; body = body_init(n_download_ucode_in_progress.header, &header_lines); n_download_ucode_in_progress.body = body; time_screen_driver(&n_download_ucode_in_progress, time, 0); pid = fork(); if (pid) { do { done = waitpid(pid, &status, WNOHANG); sleep(1); time++; time_screen_driver(&n_download_ucode_in_progress, time, 0); } while (done == 0); } else { if (dev->scsi_dev_data && dev->scsi_dev_data->type == IPR_TYPE_ADAPTER) ipr_update_ioa_fw(dev->ioa, fw_image, 1); else ipr_update_disk_fw(dev, fw_image, 1); exit(0); } ipr_get_fw_version(dev, release_level); fw_version = release_level[0] << 24 | release_level[1] << 16 | release_level[2] << 8 | release_level[3]; if (fw_version != fw_image->version) rc = 67 | EXIT_FLAG; free(n_download_ucode_in_progress.body); n_download_ucode_in_progress.body = NULL; return rc; } /** * process_choose_ucode - * @dev: ipr dev struct * * Returns: * 0 if success / 67 | EXIT_FLAG on failure **/ int process_choose_ucode(struct ipr_dev *dev) { struct ipr_fw_images *list; struct ipr_fw_images *fw_image; char *body = NULL; int header_lines = 0; int list_count, rc, i; char buffer[256]; int found = 0; i_container *i_con_head_saved; i_container *i_con = NULL; i_container *temp_i_con; char *input; int version_swp; char *version; struct screen_output *s_out; u8 release_level[4]; if (!dev->scsi_dev_data) return 67 | EXIT_FLAG; if (dev->scsi_dev_data->type == IPR_TYPE_ADAPTER) rc = get_ioa_firmware_image_list(dev->ioa, &list); else if (dev->scsi_dev_data->type == TYPE_ENCLOSURE || dev->scsi_dev_data->type == TYPE_PROCESSOR) rc = get_ses_firmware_image_list(dev, &list); else rc = get_dasd_firmware_image_list(dev, &list); if (rc < 0) return 67 | EXIT_FLAG; else list_count = rc; ipr_get_fw_version(dev, release_level); if (dev->scsi_dev_data && dev->scsi_dev_data->type == IPR_TYPE_ADAPTER) { sprintf(buffer, _("Adapter to download: %-8s %-16s\n"), dev->scsi_dev_data->vendor_id, dev->scsi_dev_data->product_id); body = add_string_to_body(NULL, buffer, "", &header_lines); sprintf(buffer, _("Adapter Location: %s.%d/\n"), dev->ioa->pci_address, dev->ioa->host_num); } else { sprintf(buffer, _("Device to download: %-8s %-16s\n"), dev->scsi_dev_data->vendor_id, dev->scsi_dev_data->product_id); body = add_string_to_body(NULL, buffer, "", &header_lines); sprintf(buffer, _("Device Location: %d:%d:%d\n"), dev->res_addr[0].bus, dev->res_addr[0].target, dev->res_addr[0].lun); } body = add_string_to_body(body, buffer, "", &header_lines); if (isprint(release_level[0]) && isprint(release_level[1]) && isprint(release_level[2]) && isprint(release_level[3])) sprintf(buffer, "%s%02X%02X%02X%02X (%c%c%c%c)\n\n", _("The current microcode for this device is: "), release_level[0], release_level[1], release_level[2], release_level[3], release_level[0], release_level[1], release_level[2], release_level[3]); else sprintf(buffer, "%s%02X%02X%02X%02X\n\n", _("The current microcode for this device is: "), release_level[0], release_level[1], release_level[2], release_level[3]); body = add_string_to_body(body, buffer, "", &header_lines); body = __body_init(body, n_choose_ucode.header, &header_lines); i_con_head_saved = i_con_head; i_con_head = i_con = NULL; if (list_count) { for (i = 0; i < list_count; i++) { version_swp = htonl(list[i].version); version = (char *)&version_swp; if (isprint(version[0]) && isprint(version[1]) && isprint(version[2]) && isprint(version[3])) sprintf(buffer," %%1 %.8X (%c%c%c%c) %s %s",list[i].version, version[0], version[1], version[2], version[3], list[i].date, list[i].file); else sprintf(buffer," %%1 %.8X %s %s",list[i].version, list[i].date, list[i].file); body = add_line_to_body(body, buffer, NULL); i_con = add_i_con(i_con, "\0", &list[i]); } } else { body = add_line_to_body(body,"(No available images)",NULL); } n_choose_ucode.body = body; s_out = screen_driver(&n_choose_ucode, header_lines, i_con); free(s_out); free(n_choose_ucode.body); n_choose_ucode.body = NULL; for_each_icon(temp_i_con) { fw_image =(struct ipr_fw_images *)(temp_i_con->data); if (dev == NULL) continue; input = temp_i_con->field_data; if (input == NULL) continue; if (strcmp(input, "1") == 0) { found++; break; } } if (!found) goto leave; body = body_init(n_confirm_download_ucode.header, &header_lines); version_swp = htonl(fw_image->version); version = (char *)&version_swp; if (isprint(version[0]) && isprint(version[1]) && isprint(version[2]) && isprint(version[3])) sprintf(buffer," 1 %.8X (%c%c%c%c) %s %s\n",fw_image->version, version[0], version[1], version[2], version[3], fw_image->date, fw_image->file); else sprintf(buffer," 1 %.8X %s %s\n",fw_image->version,fw_image->date, fw_image->file); body = add_line_to_body(body, buffer, NULL); n_confirm_download_ucode.body = body; s_out = screen_driver(&n_confirm_download_ucode, header_lines, i_con); free(n_confirm_download_ucode.body); n_confirm_download_ucode.body = NULL; if (!s_out->rc) rc = update_ucode(dev, fw_image); free(s_out); leave: free(list); i_con = free_i_con(i_con); i_con_head = i_con_head_saved; return rc; } /** * choose_ucode - * @i_con: i_container struct * * Returns: * 0 if success / 67 | EXIT_FLAG on failure **/ int choose_ucode(i_container * i_con) { i_container *temp_i_con; struct ipr_dev *dev; char *input; int rc; int dev_num = 0; if (!i_con) return 1; for_each_icon(temp_i_con) { if (!temp_i_con->data) continue; if (!temp_i_con->field_data) continue; dev =(struct ipr_dev *)(temp_i_con->data); input = temp_i_con->field_data; if (strcmp(input, "1") == 0) { rc = process_choose_ucode(dev); if (rc & EXIT_FLAG) return rc; dev_num++; } } if (dev_num == 0) /* Invalid option. No devices selected. */ return 17; return 0; } #define MAX_CMD_LENGTH 1000 /** * log_menu - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int log_menu(i_container *i_con) { char cmnd[MAX_CMD_LENGTH]; int rc, loop; struct stat file_stat; struct screen_output *s_out; int offset = 0; sprintf(cmnd,"%s/boot.msg",log_root_dir); rc = stat(cmnd, &file_stat); if (rc) /* file does not exist - do not display option */ offset--; for (loop = 0; loop < (n_log_menu.num_opts + offset); loop++) { n_log_menu.body = ipr_list_opts(n_log_menu.body, n_log_menu.options[loop].key, n_log_menu.options[loop].list_str); } n_log_menu.body = ipr_end_list(n_log_menu.body); s_out = screen_driver(&n_log_menu, 0, NULL); free(n_log_menu.body); n_log_menu.body = NULL; rc = s_out->rc; i_con = s_out->i_con; i_con = free_i_con(i_con); free(s_out); return rc; } /** * ibm_storage_log_tail - * @i_con: i_container struct * * Returns: * 1 if no status / 65 on failure **/ int ibm_storage_log_tail(i_container *i_con) { char cmnd[MAX_CMD_LENGTH]; int rc, len; def_prog_mode(); rc = system("rm -rf "_PATH_TMP".ipr.err; mkdir "_PATH_TMP".ipr.err"); if (rc != 0) { len = sprintf(cmnd, "cd %s; zcat -f messages", log_root_dir); len += sprintf(cmnd + len," | grep ipr | "); len += sprintf(cmnd + len, "sed \"s/ `hostname -s` kernel: ipr//g\" | "); len += sprintf(cmnd + len, "sed \"s/ localhost kernel: ipr//g\" | "); len += sprintf(cmnd + len, "sed \"s/ kernel: ipr//g\" | "); len += sprintf(cmnd + len, "sed \"s/\\^M//g\" | "); len += sprintf(cmnd + len, FAILSAFE_EDITOR); closelog(); openlog("iprconfig", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); syslog(LOG_ERR, "Error encountered concatenating log files...\n"); syslog(LOG_ERR, "Using failsafe editor...\n"); closelog(); openlog("iprconfig", LOG_PID | LOG_CONS, LOG_USER); sleep(3); } else { len = sprintf(cmnd,"cd %s; zcat -f messages | grep ipr |", log_root_dir); len += sprintf(cmnd + len, "sed \"s/ `hostname -s` kernel: ipr//g\""); len += sprintf(cmnd + len, " | sed \"s/ localhost kernel: ipr//g\""); len += sprintf(cmnd + len, " | sed \"s/ kernel: ipr//g\""); len += sprintf(cmnd + len, " | sed \"s/\\^M//g\" "); len += sprintf(cmnd + len, ">> "_PATH_TMP".ipr.err/errors"); system(cmnd); sprintf(cmnd, "%s "_PATH_TMP".ipr.err/errors", editor); } rc = system(cmnd); if ((rc != 0) && (rc != 127)) { /* "Editor returned %d. Try setting the default editor" */ s_status.num = rc; return 65; } else /* return with no status */ return 1; } /** * select_log_file - * @dir_entry: dirent struct * * Returns: * 1 if "messages" if found in the dir_entry->d_name / 0 otherwise **/ static int select_log_file(const struct dirent *dir_entry) { if (dir_entry) { if (strstr(dir_entry->d_name, "messages") == dir_entry->d_name) return 1; } return 0; } /** * compare_log_file - * @log_file1: * @log_file2: * * Returns: * 0 if success / non-zero on failure FIXME **/ static int compare_log_file(const struct dirent **first_dir, const struct dirent **second_dir) { char name1[MAX_CMD_LENGTH], name2[MAX_CMD_LENGTH]; struct stat stat1, stat2; if (strcmp((*first_dir)->d_name, "messages") == 0) return 1; if (strcmp((*second_dir)->d_name, "messages") == 0) return -1; sprintf(name1, "%s/%s", log_root_dir, (*first_dir)->d_name); sprintf(name2, "%s/%s", log_root_dir, (*second_dir)->d_name); if (stat(name1, &stat1)) return 1; if (stat(name2, &stat2)) return -1; if (stat1.st_mtime < stat2.st_mtime) return -1; if (stat1.st_mtime > stat2.st_mtime) return 1; return 0; } /** * ibm_storage_log - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int ibm_storage_log(i_container *i_con) { char cmnd[MAX_CMD_LENGTH]; int rc, i; struct dirent **log_files; struct dirent **dirent; int num_dir_entries; int len; def_prog_mode(); num_dir_entries = scandir(log_root_dir, &log_files, select_log_file, compare_log_file); if (num_dir_entries < 0) { s_status.num = 75; return 75; } rc = system("rm -rf "_PATH_TMP".ipr.err; mkdir "_PATH_TMP".ipr.err"); if (num_dir_entries) dirent = log_files; if (rc != 0) { len = sprintf(cmnd, "cd %s; zcat -f ", log_root_dir); /* Probably have a read-only root file system */ for (i = 0; i < num_dir_entries; i++) { len += sprintf(cmnd + len, "%s ", (*dirent)->d_name); dirent++; } len += sprintf(cmnd + len," | grep ipr-err | "); len += sprintf(cmnd + len, "sed \"s/ `hostname -s` kernel: ipr//g\" | "); len += sprintf(cmnd + len, "sed \"s/ localhost kernel: ipr//g\" | "); len += sprintf(cmnd + len, "sed \"s/ kernel: ipr//g\" | "); len += sprintf(cmnd + len, "sed \"s/\\^M//g\" | "); len += sprintf(cmnd + len, FAILSAFE_EDITOR); closelog(); openlog("iprconfig", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); syslog(LOG_ERR, "Error encountered concatenating log files...\n"); syslog(LOG_ERR, "Using failsafe editor...\n"); closelog(); openlog("iprconfig", LOG_PID | LOG_CONS, LOG_USER); sleep(3); } else { for (i = 0; i < num_dir_entries; i++) { len = sprintf(cmnd,"cd %s; zcat -f %s | grep ipr |", log_root_dir, (*dirent)->d_name); len += sprintf(cmnd + len, "sed \"s/ `hostname -s` kernel: ipr//g\""); len += sprintf(cmnd + len, " | sed \"s/ localhost kernel: ipr//g\""); len += sprintf(cmnd + len, " | sed \"s/ kernel: ipr//g\""); len += sprintf(cmnd + len, " | sed \"s/\\^M//g\" "); len += sprintf(cmnd + len, ">> "_PATH_TMP".ipr.err/errors"); system(cmnd); dirent++; } sprintf(cmnd, "%s "_PATH_TMP".ipr.err/errors", editor); } rc = system(cmnd); if (num_dir_entries) { while (num_dir_entries--) free(log_files[num_dir_entries]); free(log_files); } if ((rc != 0) && (rc != 127)) { /* "Editor returned %d. Try setting the default editor" */ s_status.num = rc; return 65; } else /* return with no status */ return 1; } /** * kernel_log - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int kernel_log(i_container *i_con) { char cmnd[MAX_CMD_LENGTH]; int rc, i; struct dirent **log_files; struct dirent **dirent; int num_dir_entries; int len; def_prog_mode(); num_dir_entries = scandir(log_root_dir, &log_files, select_log_file, compare_log_file); if (num_dir_entries < 0) { s_status.num = 75; return 75; } if (num_dir_entries) dirent = log_files; rc = system("rm -rf "_PATH_TMP".ipr.err; mkdir "_PATH_TMP".ipr.err"); if (rc != 0) { /* Probably have a read-only root file system */ len = sprintf(cmnd, "cd %s; zcat -f ", log_root_dir); /* Probably have a read-only root file system */ for (i = 0; i < num_dir_entries; i++) { len += sprintf(cmnd + len, "%s ", (*dirent)->d_name); dirent++; } len += sprintf(cmnd + len, " | sed \"s/\\^M//g\" "); len += sprintf(cmnd + len, "| %s", FAILSAFE_EDITOR); closelog(); openlog("iprconfig", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); syslog(LOG_ERR, "Error encountered concatenating log files...\n"); syslog(LOG_ERR, "Using failsafe editor...\n"); openlog("iprconfig", LOG_PID | LOG_CONS, LOG_USER); sleep(3); } else { for (i = 0; i < num_dir_entries; i++) { sprintf(cmnd, "cd %s; zcat -f %s | sed \"s/\\^M//g\" >> "_PATH_TMP".ipr.err/errors", log_root_dir, (*dirent)->d_name); system(cmnd); dirent++; } sprintf(cmnd, "%s "_PATH_TMP".ipr.err/errors", editor); } rc = system(cmnd); if (num_dir_entries > 0) { while (num_dir_entries--) free(log_files[num_dir_entries]); free(log_files); } if ((rc != 0) && (rc != 127)) { /* "Editor returned %d. Try setting the default editor" */ s_status.num = rc; return 65; } else /* return with no status */ return 1; } /** * iprconfig_log - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int iprconfig_log(i_container *i_con) { char cmnd[MAX_CMD_LENGTH]; int rc, i; struct dirent **log_files; struct dirent **dirent; int num_dir_entries; int len; def_prog_mode(); num_dir_entries = scandir(log_root_dir, &log_files, select_log_file, compare_log_file); if (num_dir_entries < 0) { s_status.num = 75; return 75; } rc = system("rm -rf "_PATH_TMP".ipr.err; mkdir "_PATH_TMP".ipr.err"); if (num_dir_entries) dirent = log_files; if (rc != 0) { len = sprintf(cmnd, "cd %s; zcat -f ", log_root_dir); /* Probably have a read-only root file system */ for (i = 0; i < num_dir_entries; i++) { len += sprintf(cmnd + len, "%s ", (*dirent)->d_name); dirent++; } len += sprintf(cmnd + len," | grep iprconfig | "); len += sprintf(cmnd + len, FAILSAFE_EDITOR); closelog(); openlog("iprconfig", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); syslog(LOG_ERR, "Error encountered concatenating log files...\n"); syslog(LOG_ERR, "Using failsafe editor...\n"); openlog("iprconfig", LOG_PID | LOG_CONS, LOG_USER); sleep(3); } else { for (i = 0; i < num_dir_entries; i++) { len = sprintf(cmnd,"cd %s; zcat -f %s | grep iprconfig ", log_root_dir, (*dirent)->d_name); len += sprintf(cmnd + len, ">> "_PATH_TMP".ipr.err/errors"); system(cmnd); dirent++; } sprintf(cmnd, "%s "_PATH_TMP".ipr.err/errors", editor); } rc = system(cmnd); if (num_dir_entries) { while (num_dir_entries--) free(log_files[num_dir_entries]); free(log_files); } if ((rc != 0) && (rc != 127)) { /* "Editor returned %d. Try setting the default editor" */ s_status.num = rc; return 65; } else /* return with no status */ return 1; } /** * kernel_root - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int kernel_root(i_container *i_con) { int rc; struct screen_output *s_out; char *body = NULL; i_con = free_i_con(i_con); /* i_con to return field data */ i_con = add_i_con(i_con,"",NULL); body = body_init(n_kernel_root.header, NULL); body = add_line_to_body(body,_("Current root directory"), log_root_dir); body = add_line_to_body(body,_("New root directory"), "%39"); n_kernel_root.body = body; s_out = screen_driver(&n_kernel_root, 0, i_con); free(n_kernel_root.body); n_kernel_root.body = NULL; rc = s_out->rc; free(s_out); return rc; } /** * confirm_kernel_root - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int confirm_kernel_root(i_container *i_con) { int rc; DIR *dir; char *input; struct screen_output *s_out; char *body = NULL; input = strip_trailing_whitespace(i_con->field_data); if (strlen(input) == 0) return (EXIT_FLAG | 61); dir = opendir(input); if (dir == NULL) /* "Invalid directory" */ return 59; else { closedir(dir); if (strcmp(log_root_dir, input) == 0) /*"Root directory unchanged"*/ return (EXIT_FLAG | 61); } body = body_init(n_confirm_kernel_root.header, NULL); body = add_line_to_body(body,_("New root directory"), input); n_confirm_kernel_root.body = body; s_out = screen_driver(&n_confirm_kernel_root, 0, i_con); free(n_confirm_kernel_root.body); n_confirm_kernel_root.body = NULL; rc = s_out->rc; if (rc == 0) { /*"Root directory changed to %s"*/ strcpy(log_root_dir, i_con->field_data); s_status.str = log_root_dir; rc = (EXIT_FLAG | 60); } else /*"Root directory unchanged"*/ rc = (EXIT_FLAG | 61); free(s_out); return rc; } /** * set_default_editor - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int set_default_editor(i_container *i_con) { int rc = 0; struct screen_output *s_out; char *body = NULL; i_con = free_i_con(i_con); i_con = add_i_con(i_con,"",NULL); body = body_init(n_set_default_editor.header, NULL); body = add_line_to_body(body, _("Current editor"), editor); body = add_line_to_body(body, _("New editor"), "%39"); n_set_default_editor.body = body; s_out = screen_driver(&n_set_default_editor, 0, i_con); free(n_set_default_editor.body); n_set_default_editor.body = NULL; rc = s_out->rc; free(s_out); return rc; } /** * confirm_set_default_editor - * @i_con: i_container struct * * Returns: * EXIT_FLAG | 62 if editor changed / EXIT_FLAG | 63 if editor unchanged **/ int confirm_set_default_editor(i_container *i_con) { int rc; char *input; struct screen_output *s_out; char *body = NULL; input = strip_trailing_whitespace(i_con->field_data); if (strlen(input) == 0) /*"Editor unchanged"*/ return (EXIT_FLAG | 63); if (strcmp(editor, input) == 0) /*"Editor unchanged"*/ return (EXIT_FLAG | 63); body = body_init(n_confirm_set_default_editor.header, NULL); body = add_line_to_body(body,_("New editor"), input); n_confirm_set_default_editor.body = body; s_out = screen_driver(&n_confirm_set_default_editor, 0, i_con); free(n_confirm_set_default_editor.body); n_confirm_set_default_editor.body = NULL; rc = s_out->rc; if (rc == 0) { /*"Editor changed to %s"*/ strcpy(editor, i_con->field_data); s_status.str = editor; rc = (EXIT_FLAG | 62); } else /*"Editor unchanged"*/ rc = (EXIT_FLAG | 63); free(s_out); return rc; } /** * restore_log_defaults - * @i_con: i_container struct * * Returns: * 64 **/ int restore_log_defaults(i_container *i_con) { strcpy(log_root_dir, DEFAULT_LOG_DIR); strcpy(editor, DEFAULT_EDITOR); return 64; /*"Default log values restored"*/ } /** * ibm_boot_log - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int ibm_boot_log(i_container *i_con) { char cmnd[MAX_CMD_LENGTH]; int rc; int len; struct stat file_stat; sprintf(cmnd,"%s/boot.msg",log_root_dir); rc = stat(cmnd, &file_stat); if (rc) return 2; /* "Invalid option specified" */ def_prog_mode(); rc = system("rm -rf "_PATH_TMP".ipr.err; mkdir "_PATH_TMP".ipr.err"); if (rc != 0) { len = sprintf(cmnd, "cd %s; zcat -f ", log_root_dir); /* Probably have a read-only root file system */ len += sprintf(cmnd + len, "boot.msg"); len += sprintf(cmnd + len," | grep ipr |"); len += sprintf(cmnd + len, FAILSAFE_EDITOR); closelog(); openlog("iprconfig", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); syslog(LOG_ERR, "Error encountered concatenating log files...\n"); syslog(LOG_ERR, "Using failsafe editor...\n"); closelog(); openlog("iprconfig", LOG_PID | LOG_CONS, LOG_USER); sleep(3); } else { len = sprintf(cmnd,"cd %s; zcat -f boot.msg | grep ipr | " "sed 's/<[0-9]>ipr-err: //g' | sed 's/<[0-9]>ipr: //g'", log_root_dir); len += sprintf(cmnd + len, ">> "_PATH_TMP".ipr.err/errors"); system(cmnd); sprintf(cmnd, "%s "_PATH_TMP".ipr.err/errors", editor); } rc = system(cmnd); if ((rc != 0) && (rc != 127)) { s_status.num = rc; return 65; /* "Editor returned %d. Try setting the default editor" */ } else return 1; /* return with no status */ } int get_ses_ioport_status(struct ipr_dev *ses) { struct ipr_query_io_port io_status; int rc = ipr_query_io_dev_port(ses, &io_status); if (!rc) ses->active_suspend = io_status.port_state; else ses->active_suspend = IOA_DEV_PORT_UNKNOWN; return rc; } /** * get_status - * @dev: ipr dev struct * @buf: data buffer * @percent: * @path_status: * * Returns: * nothing **/ static void get_status(struct ipr_dev *dev, char *buf, int percent, int path_status) { struct scsi_dev_data *scsi_dev_data = dev->scsi_dev_data; struct ipr_ioa *ioa = dev->ioa; struct ipr_query_res_state res_state; int status, rc; int format_req = 0; int blk_size = 0; struct ipr_mode_pages mode_pages; struct ipr_block_desc *block_desc; struct sense_data_t sense_data; struct ipr_cmd_status cmd_status; struct ipr_cmd_status_record *status_record; int percent_cmplt = 0; int format_in_progress = 0; int resync_in_progress = 0; struct ipr_res_redundancy_info info; if (scsi_dev_data && scsi_dev_data->type == IPR_TYPE_ADAPTER && dev == &ioa->ioa) { if (!scsi_dev_data->online) sprintf(buf, "Offline"); else if (ioa->ioa_dead) sprintf(buf, "Not Operational"); else if (ioa->nr_ioa_microcode) sprintf(buf, "Not Ready"); else sprintf(buf, "Operational"); } else if (scsi_dev_data && scsi_dev_data->type == IPR_TYPE_EMPTY_SLOT) { sprintf(buf, "Empty"); } else { if (ipr_is_volume_set(dev) || ipr_is_array(dev)) { rc = ipr_query_command_status(&ioa->ioa, &cmd_status); if (!rc) { for_each_cmd_status(status_record, &cmd_status) { if (dev->array_id != status_record->array_id) continue; if (status_record->status != IPR_CMD_STATUS_IN_PROGRESS) continue; if (status_record->command_code == IPR_RESYNC_ARRAY_PROTECTION) resync_in_progress = 1; percent_cmplt = status_record->percent_complete; } } } else if (ipr_is_af_dasd_device(dev)) { rc = ipr_query_command_status(dev, &cmd_status); if ((rc == 0) && (cmd_status.num_records != 0)) { status_record = cmd_status.record; if (status_record->status != IPR_CMD_STATUS_SUCCESSFUL && status_record->status != IPR_CMD_STATUS_FAILED) { percent_cmplt = status_record->percent_complete; format_in_progress++; } } } else if (ipr_is_gscsi(dev)) { /* Send Test Unit Ready to find percent complete in sense data. */ rc = ipr_test_unit_ready(dev, &sense_data); if ((rc == CHECK_CONDITION) && ((sense_data.error_code & 0x7F) == 0x70) && ((sense_data.sense_key & 0x0F) == 0x02) && /* NOT READY */ (sense_data.add_sense_code == 0x04) && /* LOGICAL UNIT NOT READY */ (sense_data.add_sense_code_qual == 0x04)) {/* FORMAT IN PROGRESS */ percent_cmplt = ((int)sense_data.sense_key_spec[1]*100)/0x100; format_in_progress++; } } memset(&res_state, 0, sizeof(res_state)); if (ipr_is_af(dev)) { rc = ipr_query_resource_state(dev, &res_state); if (rc != 0) res_state.not_oper = 1; } else if (ipr_is_gscsi(dev)) { format_req = 0; /* Issue mode sense/mode select to determine if device needs to be formatted */ status = ipr_mode_sense(dev, 0x0a, &mode_pages); if (status == CHECK_CONDITION && sense_data.add_sense_code == 0x31 && sense_data.add_sense_code_qual == 0x00) { format_req = 1; } else { if (!status) { if (mode_pages.hdr.block_desc_len > 0) { block_desc = (struct ipr_block_desc *)(mode_pages.data); blk_size = block_desc->block_length[2] + (block_desc->block_length[1] << 8) + (block_desc->block_length[0] << 16); if ((blk_size != IPR_JBOD_BLOCK_SIZE) && (blk_size != IPR_JBOD_4K_BLOCK_SIZE)) format_req = 1; } } /* check device with test unit ready */ rc = ipr_test_unit_ready(dev, &sense_data); if (rc == CHECK_CONDITION && sense_data.add_sense_code == 0x31 && sense_data.add_sense_code_qual == 0x00) format_req = 1; else if (rc) res_state.not_oper = 1; } } if (path_status) rc = ipr_query_res_redundancy_info(dev, &info); if (ioa->ioa_dead) sprintf(buf, "Unknown"); else if (path_status && !rc) { if (info.healthy_paths > 1) sprintf(buf, "Redundant Paths"); else if (info.healthy_paths) sprintf(buf, "Single Path"); else sprintf(buf, "No Paths"); } else if (format_in_progress) sprintf(buf, "%d%% Formatted", percent_cmplt); else if (ioa->sis64 && ipr_is_remote_af_dasd_device(dev)) { if (!scsi_dev_data) sprintf(buf, "Missing"); else sprintf(buf, "Remote"); } else if (!ioa->sis64 && ioa->is_secondary && !scsi_dev_data) sprintf(buf, "Remote"); else if (!scsi_dev_data) sprintf(buf, "Missing"); else if (!scsi_dev_data->online) { if (dev->scsi_dev_data->type == TYPE_ENCLOSURE) { get_ses_ioport_status(dev); if (dev->active_suspend == IOA_DEV_PORT_SUSPEND) sprintf(buf, "Suspend"); else sprintf(buf, "Offline"); } } else if (res_state.not_oper || res_state.not_func) sprintf(buf, "Failed"); else if (res_state.read_write_prot) sprintf(buf, "R/W Protected"); else if (res_state.prot_dev_failed) sprintf(buf, "Failed"); else if (ipr_is_volume_set(dev) || ipr_is_array(dev)) { if (res_state.prot_resuming) { if (!percent || (percent_cmplt == 0)) sprintf(buf, "Rebuilding"); else sprintf(buf, "%d%% Rebuilt", percent_cmplt); } else if (resync_in_progress) { if (!percent || (percent_cmplt == 0)) sprintf(buf, "Checking"); else sprintf(buf, "%d%% Checked", percent_cmplt); } else if (res_state.prot_suspended) sprintf(buf, "Degraded"); else if (res_state.degraded_oper || res_state.service_req) sprintf(buf, "Degraded"); else if ((dev->ioa->asymmetric_access && dev->array_rcd->current_asym_access_state == IPR_ACTIVE_OPTIMIZED) || (!dev->ioa->is_secondary && !dev->ioa->asymmetric_access)) sprintf(buf, "Optimized"); else sprintf(buf, "Non-Optimized"); } else if (format_req) sprintf(buf, "Format Required"); else if (ipr_device_is_zeroed(dev)) sprintf(buf, "Zeroed"); else sprintf(buf, "Active"); } } static const struct { u8 status; char *desc; } path_status_desc[] = { { IPR_PATH_CFG_NO_PROB, "Functional" }, { IPR_PATH_CFG_DEGRADED, "Degraded" }, { IPR_PATH_CFG_FAILED, "Failed" }, { IPR_PATH_CFG_SUSPECT, "Suspect" }, { IPR_PATH_NOT_DETECTED, "Missing" }, { IPR_PATH_INCORRECT_CONN, "Incorrect connect" } }; /** * get_phy_state - * @state: state value * * Returns: * state description **/ static const char *get_phy_state(u8 state) { int i; u8 mstate = state & IPR_PATH_CFG_STATUS_MASK; for (i = 0; i < ARRAY_SIZE(path_status_desc); i++) { if (path_status_desc[i].status == mstate) return path_status_desc[i].desc; } return "Unknown"; } static const struct { u8 type; char *desc; } path_type_desc[] = { { IPR_PATH_CFG_IOA_PORT, "IOA port" }, { IPR_PATH_CFG_EXP_PORT, "Expander port" }, { IPR_PATH_CFG_DEVICE_PORT, "Device port" }, { IPR_PATH_CFG_DEVICE_LUN, "Device LUN" } }; /** * get_phy_type - * @type: type value * * Returns: * type description **/ static const char *get_phy_type(u8 type) { int i; u8 mtype = type & IPR_PATH_CFG_TYPE_MASK; for (i = 0; i < ARRAY_SIZE(path_type_desc); i++) { if (path_type_desc[i].type == mtype) return path_type_desc[i].desc; } return "Unknown"; } static const char *link_rate[] = { "Enabled", "Disabled", "Reset Prob", "SpinupHold", "Port Selec", "Resetting", "Unknown", "Unknown", "1.5Gbps", "3.0Gbps", "6.0Gbps", "Enabled", "Enabled", "Enabled", "Enabled", "Enabled" }; static const struct { u8 state; char *desc; } path_state_desc[] = { { IPR_PATH_STATE_NO_INFO, "Unknown" }, { IPR_PATH_HEALTHY, "Healthy" }, { IPR_PATH_DEGRADED, "Degraded" }, { IPR_PATH_FAILED, "Failed" } }; /** * get_path_state - * @path_state: path_state value * * Returns: * path state description **/ static const char *get_path_state(u8 path_state) { int i; u8 state = path_state & IPR_PATH_STATE_MASK; for (i = 0; i < ARRAY_SIZE(path_state_desc); i++) { if (path_state_desc[i].state == state) return path_state_desc[i].desc; } return "Unknown"; } /** * print_phy - * @cfg: ipr_fabric_config_element struct * @body: message buffer * * Returns: * body **/ static char *print_phy(struct ipr_fabric_config_element *cfg, char *body) { int len = strlen(body); body = realloc(body, len + 256); body[len] = '\0'; len += sprintf(body + len, "%2X/%08X%08X ", cfg->phy, ntohl(cfg->wwid[0]), ntohl(cfg->wwid[1])); len += sprintf(body + len, " %-17s ", get_phy_type(cfg->type_status)); len += sprintf(body + len, " %-17s", get_phy_state(cfg->type_status)); len += sprintf(body + len, " %s \n", link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK]); return body; } /** * print_phy64 - * @cfg: ipr_fabric_config_element struct * @body: message buffer * * Returns: * body **/ static char *print_phy64(struct ipr_fabric_config_element64 *cfg, char *body, int res_path_len) { int i, ff_len; int len = strlen(body); char buffer[IPR_MAX_RES_PATH_LEN]; body = realloc(body, len + 256); body[len] = '\0'; ipr_format_res_path_wo_hyphen(cfg->res_path, buffer, IPR_MAX_RES_PATH_LEN); ff_len = res_path_len - strlen(buffer); for ( i = 0; i < ff_len; i++) strncat(buffer, "F", strlen("F")); len += sprintf(body + len, "%s", buffer); len += sprintf(body + len, "/%08X%08X", ntohl(cfg->wwid[0]), ntohl(cfg->wwid[1])); len += sprintf(body + len, " %-17s ", get_phy_type(cfg->type_status)); len += sprintf(body + len, " %-17s", get_phy_state(cfg->type_status)); len += sprintf(body + len, " %s \n", link_rate[cfg->link_rate & IPR_PHY_LINK_RATE_MASK]); return body; } /** * print_path - * @ioa: ipr_fabric_descriptor struct * @body: message buffer * * Returns: * body **/ static char *print_path(struct ipr_fabric_descriptor *fabric, char *body) { int len = strlen(body); struct ipr_fabric_config_element *cfg; body = realloc(body, len + 256); body[len] = '\0'; len += sprintf(body + len, "%2X/ " "Physical Path ", fabric->ioa_port); if (fabric->path_state & IPR_PATH_ACTIVE) len += sprintf(body + len, " Yes "); else if (fabric->path_state & IPR_PATH_NOT_ACTIVE) len += sprintf(body + len, " No "); else len += sprintf(body + len, " ??? "); len += sprintf(body + len, "%-17s \n", get_path_state(fabric->path_state)); for_each_fabric_cfg(fabric, cfg) body = print_phy(cfg, body); return body; } /** * print_path64 - * @ioa: ipr_fabric_descriptor64 struct * @body: message buffer * * Returns: * body **/ static char *print_path64(struct ipr_fabric_descriptor64 *fabric, char *body) { int len = strlen(body); struct ipr_fabric_config_element64 *cfg; char buffer[IPR_MAX_RES_PATH_LEN]; int max_res_path_len = 0; body = realloc(body, len + 256); body[len] = '\0'; ipr_format_res_path_wo_hyphen(fabric->res_path, buffer, IPR_MAX_RES_PATH_LEN); len += sprintf(body + len, "%s " "Resource Path ", buffer); if (fabric->path_state & IPR_PATH_ACTIVE) len += sprintf(body + len, " Yes "); else if (fabric->path_state & IPR_PATH_NOT_ACTIVE) len += sprintf(body + len, " No "); else len += sprintf(body + len, " ??? "); len += sprintf(body + len, "%-17s \n", get_path_state(fabric->path_state)); for_each_fabric_cfg(fabric, cfg) { ipr_format_res_path_wo_hyphen(cfg->res_path, buffer, IPR_MAX_RES_PATH_LEN); if (strlen(buffer) > max_res_path_len) max_res_path_len = strlen(buffer); } for_each_fabric_cfg(fabric, cfg) { body = print_phy64(cfg, body, max_res_path_len); } return body; } /** * print_path_details - * @dev: ipr dev struct * @body: message buffer * * Returns: * body **/ static char *print_path_details(struct ipr_dev *dev, char *body) { int rc, len = 0; struct ipr_res_redundancy_info info; struct ipr_fabric_descriptor *fabric; struct ipr_fabric_descriptor64 *fabric64; rc = ipr_query_res_redundancy_info(dev, &info); if (rc) return body; if (body) len = strlen(body); body = realloc(body, len + 256); body[len] = '\0'; if (dev->ioa->sis64) for_each_fabric_desc64(fabric64, &info) body = print_path64(fabric64, body); else for_each_fabric_desc(fabric, &info) body = print_path(fabric, body); return body; } /** * __print_device - * @dev: ipr dev struct * @body: message buffer * @option: * @sd: * @sg: * @vpd: * @percent: * @indent: * @res_path: * @ra: * @path_status: * * Returns: * char * buffer **/ char *__print_device(struct ipr_dev *dev, char *body, char *option, int sd, int sg, int vpd, int percent, int indent, int res_path, int ra, int path_status, int hw_loc, int conc_main, int ioa_flag, int serial_num) { u16 len = 0; struct scsi_dev_data *scsi_dev_data = dev->scsi_dev_data; int i; char *dev_name = dev->dev_name; char *gen_name = dev->gen_name; char node_name[7], buf[100], raid_str[48]; char res_path_name[IPR_MAX_RES_PATH_LEN]; int tab_stop = 0; int loc_len = 0; char vendor_id[IPR_VENDOR_ID_LEN + 1]; char product_id[IPR_PROD_ID_LEN + 1]; struct ipr_ioa *ioa = dev->ioa, *ioa_phy_loc; bool is4k = false; /* In cases where we're having problems with the device */ if (!ioa) return body; if (ioa_flag) ioa_phy_loc = dev->ioa; if (body) len = strlen(body); body = realloc(body, len + 256); if (sd && strlen(dev_name) > 5) ipr_strncpy_0(node_name, &dev_name[5], 6); else if (sg && (strlen(gen_name) > 5)) ipr_strncpy_0(node_name, &gen_name[5], 6); else node_name[0] = '\0'; if (option) len += sprintf(body + len, " %s ", option); if (scsi_dev_data && scsi_dev_data->type == TYPE_ENCLOSURE && (serial_num == 1 || hw_loc == 1)) len += sprintf(body + len, "%-8s ", node_name); else len += sprintf(body + len, "%-6s ", node_name); if (hw_loc) { if (ioa_flag) { if (strlen(ioa_phy_loc->physical_location)) { loc_len = sprintf(body + len, "%-40s ", ioa_phy_loc->physical_location); len += loc_len; } else { for (i = 0; i < 40-loc_len; i++) body[len+i] = ' '; len += 40-loc_len; } } else { if (strlen(dev->physical_location)) { if (conc_main) { loc_len = sprintf(body + len, "%-26s ", dev->physical_location); len += loc_len; } else { loc_len = sprintf(body + len, "%-38s ", dev->physical_location); len += loc_len; } } else { if (conc_main) { loc_len = sprintf(body + len, "%d/%d:", ioa->host_num,ioa->host_num); len += loc_len; } else { for (i = 0; i < 39-loc_len; i++) body[len+i] = ' '; len += 39-loc_len; } } } } else { if (res_path) { if (ioa->sis64) if (serial_num == 1) { if (scsi_dev_data && scsi_dev_data->type == IPR_TYPE_ADAPTER) loc_len = sprintf(body + len, "%s/%-28d", ioa->pci_address, ioa->host_num); else { ipr_format_res_path(dev->res_path[ra].res_path_bytes, res_path_name, IPR_MAX_RES_PATH_LEN); loc_len = sprintf(body + len, "%s/%d/%-24s", ioa->pci_address, ioa->host_num, res_path_name); } } else if (conc_main) { ipr_format_res_path(dev->res_path[ra].res_path_bytes, res_path_name, IPR_MAX_RES_PATH_LEN); loc_len = sprintf(body + len, "%d/%-26s ", ioa->host_num, res_path_name); } else loc_len = sprintf(body + len, "%-26s ",scsi_dev_data ? scsi_dev_data->res_path : ""); else /*32bit*/ if (serial_num == 1) { if (scsi_dev_data && scsi_dev_data->type == IPR_TYPE_ADAPTER) loc_len = sprintf(body + len, "%s/%-29d:", ioa->pci_address, ioa->host_num); else loc_len = sprintf(body + len, "%s/%d", ioa->pci_address, ioa->host_num); } else if (conc_main) loc_len = sprintf(body + len, "%d/%d:", ioa->host_num,ioa->host_num); else loc_len = sprintf(body + len, "%d:", ioa->host_num); len += loc_len; } else { loc_len = sprintf(body + len, "%s/%d:", ioa->pci_address, ioa->host_num); len += loc_len; } } if (scsi_dev_data && scsi_dev_data->type == IPR_TYPE_ADAPTER && dev == &ioa->ioa) { if (serial_num == 1 ) { if (!res_path || !ioa->sis64) { for (i = 0; i < 28-loc_len; i++) body[len+i] = ' '; len += 28-loc_len; } len += sprintf(body + len,"%-13s ", ioa->yl_serial_num); } else if (hw_loc == 1 ) { if (!res_path || !ioa->sis64) { for (i = 0; i < 40-loc_len; i++) body[len+i] = ' '; len += 40-loc_len; } len += sprintf(body + len,"%-16s ", scsi_dev_data->product_id); } else { if (!res_path || !ioa->sis64) { for (i = 0; i < 27-loc_len; i++) body[len+i] = ' '; len += 27-loc_len; } if (!vpd) { tab_stop = sprintf(body + len,"%s %s", get_bus_desc(ioa), get_ioa_desc(dev->ioa)); len += tab_stop; for (i = 0; i < 26-tab_stop; i++) body[len+i] = ' '; len += 26-tab_stop; } else len += sprintf(body + len,"%-8s %-16s ", scsi_dev_data->vendor_id, scsi_dev_data->product_id); } } else if (scsi_dev_data && scsi_dev_data->type == IPR_TYPE_EMPTY_SLOT) { if (!res_path || !ioa->sis64) { if (hw_loc) { for (i = 0; i < 27-loc_len; i++) body[len+i] = ' '; len += 27-loc_len; } else { tab_stop = sprintf(body + len,"%d:%d: ", dev->res_addr[ra].bus, dev->res_addr[ra].target); loc_len += tab_stop; len += tab_stop; for (i = 0; i < 29-loc_len; i++) body[len+i] = ' '; len += 29-loc_len; } } len += sprintf(body + len, "%-8s %-16s ", " ", " "); } else { if (serial_num) { if (!res_path || !ioa->sis64) { for (i = 0; i < 26-loc_len; i++) body[len+i] = ' '; len += 26-loc_len; } } else if (hw_loc) { if (!res_path || !ioa->sis64) { for (i = 0; i < 27-loc_len; i++) body[len+i] = ' '; len += 27-loc_len; } } else { if (!res_path || !ioa->sis64) { tab_stop = sprintf(body + len,"%d:%d:%d ", dev->res_addr[ra].bus, dev->res_addr[ra].target, dev->res_addr[ra].lun); loc_len += tab_stop; len += tab_stop; if (conc_main && (hw_loc != 1)) { for (i = 0; i < 29-loc_len; i++) body[len+i] = ' '; len += 29-loc_len; } else { for (i = 0; i < 27-loc_len; i++) body[len+i] = ' '; len += 27-loc_len; } } } if (vpd) { if (scsi_dev_data) { if (dev->qac_entry && ipr_is_device_record(dev->qac_entry->record_id)) { ipr_strncpy_0(vendor_id, (char *)dev->vendor_id, IPR_VENDOR_ID_LEN); ipr_strncpy_0(product_id, (char *)dev->product_id, IPR_PROD_ID_LEN); } else { ipr_strncpy_0(vendor_id, scsi_dev_data->vendor_id, IPR_VENDOR_ID_LEN); ipr_strncpy_0(product_id, scsi_dev_data->product_id, IPR_PROD_ID_LEN); } } else if (dev->qac_entry) { if (ipr_is_device_record(dev->qac_entry->record_id)) { ipr_strncpy_0(vendor_id, (char *)dev->vendor_id, IPR_VENDOR_ID_LEN); ipr_strncpy_0(product_id, (char *)dev->product_id, IPR_PROD_ID_LEN); } else if (ipr_is_vset_record(dev->qac_entry->record_id)) { ipr_strncpy_0(vendor_id, (char *)dev->vendor_id, IPR_VENDOR_ID_LEN); ipr_strncpy_0(product_id, (char *)dev->product_id, IPR_PROD_ID_LEN); } } if (hw_loc) { len += sprintf(body + len, "%-16s ", product_id); } else { len += sprintf(body + len, "%-8s %-16s ", vendor_id, product_id); } } else { if (ioa->support_4k && dev->block_dev_class & IPR_BLK_DEV_CLASS_4K) is4k = true; else is4k = false; if (ipr_is_hot_spare(dev)) { if (dev->block_dev_class & IPR_SSD) sprintf(buf, "%s%s", is4k ? "4K " : "", "SSD Hot Spare"); else sprintf(buf, "%s%s", is4k ? "4K " : "", "Hot Spare"); len += sprintf(body + len, "%-25s ", buf); } else if (ipr_is_volume_set(dev) || ipr_is_array(dev)) { if (dev->block_dev_class & IPR_SSD) sprintf(buf, "RAID %s %s SSD Disk Array", get_prot_level_str(ioa->supported_arrays, dev->raid_level), is4k ? "4K" : ""); else sprintf(buf, "RAID %s %s Disk Array", get_prot_level_str(ioa->supported_arrays, dev->raid_level), is4k ? "4K" : ""); len += sprintf(body + len, "%-25s ", buf); } else if (ipr_is_array_member(dev)) { if (indent) if (dev->block_dev_class & IPR_SSD) sprintf(raid_str," RAID %s %s SSD Member", dev->prot_level_str, is4k ? "4K" : ""); else sprintf(raid_str," RAID %s %s Array Member", dev->prot_level_str, is4k ? "4K" : ""); else if (dev->block_dev_class & IPR_SSD) sprintf(raid_str,"RAID %s %sSSD Member", dev->prot_level_str, is4k ? "4K" : ""); else sprintf(raid_str,"RAID %s %s Array Member", dev->prot_level_str, is4k ? "4K" : ""); len += sprintf(body + len, "%-25s ", raid_str); } else if (ipr_is_af_dasd_device(dev)) if (dev->block_dev_class & IPR_SSD) len += sprintf(body + len, "%-25s ", is4k ? "Advanced Function 4K SSD" : "Advanced Function SSD"); else len += sprintf(body + len, "%-25s ", is4k ? "Advanced Function 4K Disk" : "Advanced Function Disk"); else if (scsi_dev_data && scsi_dev_data->type == TYPE_ENCLOSURE) { if (serial_num == 1) len += sprintf(body + len, "%-13s ", (char *)&dev->serial_number); else len += sprintf(body + len, "%-25s ", "Enclosure"); } else if (scsi_dev_data && scsi_dev_data->type == TYPE_PROCESSOR) len += sprintf(body + len, "%-25s ", "Processor"); else if (scsi_dev_data && scsi_dev_data->type == TYPE_ROM) len += sprintf(body + len, "%-25s ", "CD/DVD"); else if (scsi_dev_data && scsi_dev_data->type == TYPE_TAPE) len += sprintf(body + len, "%-25s ", "Tape"); else if (ioa->ioa_dead) len += sprintf(body + len, "%-25s ", "Unavailable Device"); else { len += sprintf(body + len, "%-25s ", is4k ? "Physical 4K Disk" : "Physical Disk"); } } } get_status(dev, buf, percent, path_status); sprintf(body + len, "%s\n", buf); return body; } /** * print_device - * @dev: ipr dev struct * @body: message buffer * @option: * @type: bitfield - type if information to print * * Returns: * char * buffer **/ /* * Print the given device to a buffer. The type parameter is defined as * a bitfield which has the following behavior: * * 0: print sd, resource address (sis32)/resource path (sis64), device VPD * 1: print sd, pci/scsi location, device description, print % complete * 2: print sg, resource address (sis32)/resource path (sis64), device VPD * 3: print sd, pci/scsi location, device description, indent array members, print % complete * 4: print sg, pci/scsi location, device description, print % complete * 5: print sd, hardware location, status * 6: print sd, the first resource adress(sis32)/resource path(sis64), dev VPD * 7: print sd, the second resource adress(sis32)/resource path(sis64), dev VPD * 8: print sg, pci/host/resource path, serial number, status * 9: print sg, physical location, production id, status */ char *print_device(struct ipr_dev *dev, char *body, char *option, int type) { int sd, sg, vpd, percent, indent, res_path, hw_loc, conc_main, ioa_flag, serial_num; sd = sg = vpd = percent = indent = res_path = hw_loc = conc_main = ioa_flag = serial_num = 0; if ((type & 0x1000) == 0x1000) ioa_flag = 1; type &= 0x0fff; if (type == 2 || type == 4) sg = 1; else sd = 1; if (type == 0 || type == 2 || type == 6 || type == 7) { vpd = 1; res_path = 1; } else percent = 1; if (type == 3) indent = 1; if (type == 5) { hw_loc = 1; conc_main = 1; sg = 1; } if (type == 6 || type == 7) { conc_main = 1; sg = 1; } if (type == 8) { sg = 1; if (dev->ioa->sis64) res_path = 1; else res_path = 0; serial_num = 1; } if (type == 9) { sg = 1; hw_loc = 1; if (dev->ioa->sis64) res_path = 1; else res_path = 0; vpd = 1; } return __print_device(dev, body, option, sd, sg, vpd, percent, indent, res_path, type&1, 0, hw_loc, conc_main, ioa_flag, serial_num); } /** * usage - display usage information * * Returns: * nothing **/ static void usage() { printf(_("Usage: iprconfig [options]\n")); printf(_(" Options: -e name Default editor for viewing error logs\n")); printf(_(" -k dir Kernel messages root directory\n")); printf(_(" -c command Command line configuration\n")); printf(_(" See man page for complete details\n")); printf(_(" --version Print iprconfig version\n")); printf(_(" --debug Enable additional error logging\n")); printf(_(" --force Disable safety checks. See man page for details.\n")); printf(_(" Use quotes around parameters with spaces\n")); } /** * find_and_add_dev - * @name: device name * * Returns: * ipr_dev struct if success / NULL on failure **/ static struct ipr_dev *find_and_add_dev(char *name) { struct ipr_dev *dev = find_dev(name); if (!dev) { syslog(LOG_ERR, _("Invalid device specified: %s\n"), name); return NULL; } ipr_add_sysfs_dev(dev, &head_sdev, &tail_sdev); return dev; } /** * raid_create_add_dev - * @name: device name * * Returns: * 0 if success / non-zero on failure **/ static int raid_create_add_dev(char *name) { struct ipr_dev *dev = find_and_add_dev(name); if (!dev) return -EINVAL; if (ipr_is_gscsi(dev)) { if (is_af_blocked(dev, 0)) return -EINVAL; if (!is_format_allowed(dev)) return -EIO; if (dev->ioa->support_4k && dev->block_dev_class & IPR_BLK_DEV_CLASS_4K) add_format_device(dev, IPR_AF_4K_BLOCK_SIZE); else add_format_device(dev, dev->ioa->af_block_size); dev_init_tail->do_init = 1; } return 0; } /** * raid_create_check_num_devs - * @ioa: ipr_array_cap_entry struct * @num_devs: number of devices * * Returns: * 0 if success / non-zero on failure **/ static int raid_create_check_num_devs(struct ipr_array_cap_entry *cap, int num_devs, int err_type) { if (num_devs < cap->min_num_array_devices || num_devs > cap->max_num_array_devices || (num_devs % cap->min_mult_array_devices) != 0) { syslog(LOG_ERR, _("Invalid number of devices (%d) selected for " "RAID %s array.\n" "Must select a minimum of %d devices, a " "maximum of %d devices, " "and must be a multiple of %d devices.\n"), num_devs, cap->prot_level_str, cap->min_num_array_devices, cap->max_num_array_devices, cap->min_mult_array_devices); if (err_type == 0) return -EINVAL; else if (num_devs > cap->min_num_array_devices) return RC_77_Too_Many_Disks; else if (num_devs < cap->min_num_array_devices) return RC_78_Too_Few_Disks; else if ((num_devs % cap->min_num_array_devices) != 0) return RC_25_Devices_Multiple; } return 0; } /** * curses_init - initialize curses * * Returns: * nothing **/ static void curses_init() { /* makes program compatible with all terminals - originally didn't display text correctly when user was running xterm */ setenv("TERM", "vt100", 1); setlocale(LC_ALL, ""); initscr(); } /** * format_devices - * @args: argument vector * @num_args: number of arguments * @blksz: block size * * Returns: * 0 if success / non-zero on failure FIXME **/ static int format_devices(char **args, int num_args, int fmt_flag) { int i, rc, blksz; struct ipr_dev *dev; for (i = 0; i < num_args; i++) { dev = find_dev(args[i]); if (!dev) { fprintf(stderr, "Invalid device: %s\n", args[i]); continue; } if (!dev->ioa->support_4k && dev->block_dev_class & IPR_BLK_DEV_CLASS_4K) { fprintf(stderr, "Invalid device specified: %s. 4K disks not supprted on this adapter", args[i]); continue; } if (fmt_flag == IPR_FMT_JBOD ) { if (dev->ioa->support_4k && dev->block_dev_class & IPR_BLK_DEV_CLASS_4K) blksz = IPR_JBOD_4K_BLOCK_SIZE; else blksz = IPR_JBOD_BLOCK_SIZE; } else if (fmt_flag == IPR_FMT_RECOVERY) blksz = 0; if (!ipr_is_af_dasd_device(dev) && !ipr_is_gscsi(dev)) continue; if (ipr_is_af_dasd_device(dev)) { if (ipr_is_array_member(dev) && !dev->dev_rcd->no_cfgte_vol) continue; if (!is_format_allowed(dev)) continue; } else { if (!is_format_allowed(dev)) continue; } add_format_device(dev, blksz); dev_init_tail->do_init = 1; } rc = send_dev_inits(NULL); free_devs_to_init(); return IPR_XLATE_DEV_FMT_RC(rc); } /** * recovery_format - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure FIXME **/ static int recovery_format(char **args, int num_args) { return format_devices(args, num_args, IPR_FMT_RECOVERY); } /** * format_for_jbod - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure FIXME **/ static int format_for_jbod(char **args, int num_args) { return format_devices(args, num_args, IPR_FMT_JBOD); } /** * format_for_raid - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure FIXME **/ static int format_for_raid(char **args, int num_args) { int i, rc, blk_size; struct ipr_dev *dev; for (i = 0; i < num_args; i++) { dev = find_and_add_dev(args[i]); if (!dev || !can_format_for_raid(dev)) { fprintf(stderr, "Invalid device: %s\n", args[i]); return -EINVAL; } if (!dev->ioa->support_4k && dev->block_dev_class & IPR_BLK_DEV_CLASS_4K) { fprintf(stderr, "Invalid device specified: %s. 4K disks not supprted on this adapter", args[i]); return -EINVAL; } if (dev->ioa->support_4k && dev->block_dev_class & IPR_BLK_DEV_CLASS_4K) blk_size = IPR_AF_4K_BLOCK_SIZE; else blk_size = dev->ioa->af_block_size; add_format_device(dev, blk_size); dev_init_tail->do_init = 1; } rc = send_dev_inits(NULL); free_devs_to_init(); return IPR_XLATE_DEV_FMT_RC(rc); } /** * raid_create - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure FIXME **/ static int raid_create(char **args, int num_args) { int i, num_devs = 0, rc; int non_4k_count = 0, is_4k_count = 0; int next_raid_level, next_stripe_size, next_qdepth, next_label; char *raid_level = IPR_DEFAULT_RAID_LVL; char label[8]; int stripe_size, qdepth, zeroed_devs; struct ipr_dev *dev; struct sysfs_dev *sdev; struct ipr_ioa *ioa = NULL; struct ipr_array_cap_entry *cap; u8 array_id; label[0] = '\0'; next_raid_level = 0; next_stripe_size = 0; next_qdepth = 0; next_label = 0; stripe_size = 0; qdepth = 0; zeroed_devs = 0; for (i = 0; i < num_args; i++) { if (strcmp(args[i], "-z") == 0) zeroed_devs = 1; else if (strcmp(args[i], "-r") == 0) next_raid_level = 1; else if (strcmp(args[i], "-s") == 0) next_stripe_size = 1; else if (strcmp(args[i], "-q") == 0) next_qdepth = 1; else if (strcmp(args[i], "-l") == 0) next_label = 1; else if (next_raid_level) { next_raid_level = 0; raid_level = args[i]; } else if (next_stripe_size) { next_stripe_size = 0; stripe_size = strtoul(args[i], NULL, 10); } else if (next_qdepth) { next_qdepth = 0; qdepth = strtoul(args[i], NULL, 10); } else if (next_label) { next_label = 0; if (strlen(args[i]) > (sizeof(label) - 1)) { syslog(LOG_ERR, _("RAID array label too long. Maximum length is %d characters\n"), (int)(sizeof(label) - 1)); return -EINVAL; } strcpy(label, args[i]); } else if ((dev = find_dev(args[i]))) { num_devs++; if (zeroed_devs) ipr_add_zeroed_dev(dev); if (raid_create_add_dev(args[i])) return -EIO; } } for (sdev = head_sdev; sdev; sdev = sdev->next) { dev = ipr_sysfs_dev_to_dev(sdev); if (!dev) { syslog(LOG_ERR, _("Cannot find device\n")); return -EINVAL; } if (!ioa) ioa = dev->ioa; else if (ioa != dev->ioa) { syslog(LOG_ERR, _("All devices must be attached to the same adapter.\n")); return -EINVAL; } if (dev->block_dev_class & IPR_BLK_DEV_CLASS_4K) is_4k_count++; else non_4k_count++; } if (is_4k_count > 0 && non_4k_count > 0) { syslog(LOG_ERR, _("4K disks and 5XX disks can not be mixed in an array.\n")); return -EINVAL; } if (!ioa) { syslog(LOG_ERR, _("No valid devices specified.\n")); return -EINVAL; } cap = get_cap_entry(ioa->supported_arrays, raid_level); if (!cap) { syslog(LOG_ERR, _("Invalid RAID level for selected adapter. %s\n"), raid_level); return -EINVAL; } if (stripe_size && ((cap->supported_stripe_sizes & stripe_size) != stripe_size)) { syslog(LOG_ERR, _("Unsupported stripe size for selected adapter. %d\n"), stripe_size); return -EINVAL; } if ((rc = raid_create_check_num_devs(cap, num_devs, 0))) return rc; if (!stripe_size) stripe_size = ntohs(cap->recommended_stripe_size); if (!qdepth) { if (ioa->sis64) qdepth = num_devs * 16; else qdepth = num_devs * 4; } if (dev_init_head) { rc = send_dev_inits(NULL); free_devs_to_init(); if (IPR_XLATE_DEV_FMT_RC(rc)) return rc; } check_current_config(false); for (sdev = head_sdev; sdev; sdev = sdev->next) { dev = ipr_sysfs_dev_to_dev(sdev); if (!dev) { syslog(LOG_ERR, _("Cannot find device: %s\n"), sdev->sysfs_device_name); return -EIO; } if (!ipr_is_af_dasd_device(dev)) { scsi_err(dev, "Invalid device type\n"); return -EIO; } dev->dev_rcd->issue_cmd = 1; } if (!ioa->start_array_qac_entry) { scsi_err(dev, "Invalid device type\n"); return -EIO; } if (ioa->sis64) { array_id = ioa->start_array_qac_entry->type3.array_id; if (strlen(label)) strcpy((char *)ioa->start_array_qac_entry->type3.desc, label); } else array_id = ioa->start_array_qac_entry->type2.array_id; add_raid_cmd_tail(ioa, &ioa->ioa, array_id); raid_cmd_tail->qdepth = qdepth; raid_cmd_tail->do_cmd = 1; rc = ipr_start_array_protection(ioa, stripe_size, cap->prot_level); if (rc) return rc; rc = raid_start_complete(); format_done = 1; if (rc == RC_18_Array_Created) return 0; else return -EIO; } /** * hot_spare_delete - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure FIXME **/ static int hot_spare_delete(char **args, int num_args) { struct ipr_dev *dev; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } dev->dev_rcd->issue_cmd = 1; return ipr_remove_hot_spare(dev->ioa); } /** * hot_spare_create - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure FIXME **/ static int hot_spare_create(char **args, int num_args) { struct ipr_dev *dev; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } dev->dev_rcd->issue_cmd = 1; return ipr_add_hot_spare(dev->ioa); } /** * raid_include_cmd - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure FIXME **/ static int raid_include_cmd(char **args, int num_args) { int i, rc, zeroed = 0; struct ipr_dev *dev, *vset = NULL; struct ipr_ioa *ioa; struct devs_to_init_t *dev_init; for (i = 0; i < num_args; i++) { if (!strcmp(args[i], "-z")) zeroed = 1; } for (i = 0; i < num_args; i++) { if (!strcmp(args[i], "-z")) continue; dev = find_and_add_dev(args[i]); if (!dev) return -EINVAL; if (ipr_is_volume_set(dev)) { if (vset) { fprintf(stderr, "Invalid parameters. Only one " "disk array can be specified at a time.\n"); return -EINVAL; } vset = dev; ioa = vset->ioa; continue; } if (!ipr_is_af_dasd_device(dev)) { fprintf(stderr, "Invalid device specified. Device must " "be formatted to Advanced Function Format.\n"); return -EINVAL; } dev->dev_rcd->issue_cmd = 1; if (!zeroed) { if (!is_format_allowed(dev)) { fprintf(stderr, "Format not allowed to %s\n", args[i]); return -EIO; } add_format_device(dev, 0); dev_init_tail->do_init = 1; } } if (!vset) { fprintf(stderr, "Invalid parameters. A disk array must be specified.\n"); return -EINVAL; } for_each_dev_to_init(dev_init) if (vset->block_dev_class != dev_init->dev->block_dev_class) { fprintf(stderr, "Invalid parameter. Can not mix SSDs and HDDs.\n"); return -EINVAL; } if (!zeroed) { rc = send_dev_inits(NULL); free_devs_to_init(); if (IPR_XLATE_DEV_FMT_RC(rc)) return rc; } return do_array_include(vset->ioa, vset->array_id, vset->ioa->qac_data); } /** * raid_delete - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure FIXME **/ static int raid_delete(char **args, int num_args) { int rc; struct ipr_dev *dev; struct ipr_res_addr *ra; dev = find_and_add_dev(args[0]); if (!dev || !ipr_is_volume_set(dev) || dev->ioa->is_secondary) { syslog(LOG_ERR, _("Invalid device specified: %s\n"), args[0]); return -EINVAL; } dev->array_rcd->issue_cmd = 1; if (dev->scsi_dev_data) { ipr_allow_restart(dev, 0); ipr_set_manage_start_stop(dev); ipr_start_stop_stop(dev); ipr_write_dev_attr(dev, "delete", "1"); } if (dev->alt_path && dev->alt_path->scsi_dev_data) { ipr_allow_restart(dev->alt_path, 0); ipr_set_manage_start_stop(dev->alt_path); ipr_start_stop_stop(dev->alt_path); ipr_write_dev_attr(dev->alt_path, "delete", "1"); } sleep(2); rc = ipr_stop_array_protection(dev->ioa); if (rc != 0) { if (dev->scsi_dev_data) { ra = &dev->res_addr[0]; ipr_scan(dev->ioa, ra->bus, ra->target, ra->lun); ipr_allow_restart(dev, 1); } if (dev->alt_path && dev->alt_path->scsi_dev_data) { ra = &dev->alt_path->res_addr[0]; ipr_scan(dev->alt_path->ioa, ra->bus, ra->target, ra->lun); ipr_allow_restart(dev->alt_path, 1); } } return rc; } /** * printf_device - Display the given device * @dev: ipr dev struct * @type: type of information to print * * Returns: * nothing **/ static void printf_device(struct ipr_dev *dev, int type) { char *buf = print_device(dev, NULL, NULL, type); if (buf) { printf("%s", buf); free(buf); } } /** * __printf_device - * @dev: ipr dev struct * @sd: * @sg: * @vpd: * @percent: * @indent: * @res_path: * * Returns: * nothing **/ static void __printf_device(struct ipr_dev *dev, int sd, int sg, int vpd, int percent, int indent, int res_path) { char *buf = __print_device(dev, NULL, NULL, sd, sg, vpd, percent, indent, res_path, 0, 0, 0, 0, 0, 0); if (buf) { printf("%s", buf); free(buf); } } /** * printf_vsets - * @ioa: ipr ioa struct * @type: type of information to print * * Returns: * nothing **/ static void printf_vsets(struct ipr_ioa *ioa, int type) { struct ipr_dev *vset, *dev; for_each_vset(ioa, vset) { printf_device(vset, type); for_each_dev_in_vset(vset, dev) printf_device(dev, type); } } /** * printf_hot_spare_disks - * @ioa: ipr ioa struct * @type: type of information to print * * Returns: * nothing **/ static void printf_hot_spare_disks(struct ipr_ioa *ioa, int type) { struct ipr_dev *dev; for_each_hot_spare(ioa, dev) printf_device(dev, type); } /** * printf_standlone_disks - * @ioa: ipr ioa struct * @type: type of information to print * * Returns: * nothing **/ static void printf_standlone_disks(struct ipr_ioa *ioa, int type) { struct ipr_dev *dev; for_each_standalone_disk(ioa, dev) printf_device(dev, type); } /** * printf_ses_devices - * @ioa: ipr ioa struct * @type: type of information to print * * Returns: * nothing **/ static void printf_ses_devices(struct ipr_ioa *ioa, int type) { struct ipr_dev *dev; for_each_ses(ioa, dev) printf_device(dev, type); } /** * printf_ioa - * @ioa: ipr ioa struct * @type: type of information to print * * Returns: * nothing **/ static void printf_ioa(struct ipr_ioa *ioa, int type) { if (!ioa->ioa.scsi_dev_data) return; printf_device(&ioa->ioa, type); printf_standlone_disks(ioa, type); printf_hot_spare_disks(ioa, type); printf_vsets(ioa, type); printf_ses_devices(ioa, type); } /** * query_raid_create - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_raid_create(char **args, int num_args) { struct ipr_dev *dev; struct ipr_ioa *ioa; int num = 0; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } ioa = dev->ioa; if (!ioa->qac_data->num_records) return 0; for_each_disk(ioa, dev) { if (!device_supported(dev)) continue; if (ipr_is_af_dasd_device(dev) && !dev->dev_rcd->start_cand) continue; if (!num) printf("%s\n%s\n", status_hdr[2], status_sep[2]); __printf_device(dev, 1, 1, 1, 0, 0, 0); num++; } if (num) printf("\b\n"); return 0; } /** * query_raid_delete - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_raid_delete(char **args, int num_args) { struct ipr_dev *dev; struct ipr_ioa *ioa; int num = 0; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } ioa = dev->ioa; for_each_vset(ioa, dev) { if (!dev->array_rcd->stop_cand) continue; if (is_array_in_use(ioa, dev->array_id)) continue; if (!num) printf("%s\n%s\n", status_hdr[3], status_sep[3]); printf_device(dev, 3); num++; } return 0; } /** * query_hot_spare_create - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_hot_spare_create(char **args, int num_args) { struct ipr_dev *dev; struct ipr_ioa *ioa; int hdr = 0; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } ioa = dev->ioa; for_each_af_dasd(ioa, dev) { if (!dev->dev_rcd->add_hot_spare_cand) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(dev, 2); } return 0; } /** * query_hot_spare_delete - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_hot_spare_delete(char **args, int num_args) { struct ipr_dev *dev; struct ipr_ioa *ioa; int hdr = 0; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } ioa = dev->ioa; for_each_hot_spare(ioa, dev) { if (!dev->dev_rcd->rmv_hot_spare_cand) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(dev, 2); } return 0; } /** * query_raid_consistency_check - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_raid_consistency_check(char **args, int num_args) { struct ipr_dev *dev, *array; struct ipr_ioa *ioa; int hdr = 0; for_each_ioa(ioa) { for_each_array(ioa, array) { if (!array->array_rcd->resync_cand) continue; dev = array; if (ioa->sis64) dev = get_vset_from_array(ioa, dev); if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[3], status_sep[3]); } printf_device(dev, 1); } } return 0; } /** * raid_consistency_check - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int raid_consistency_check(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (dev->ioa->sis64) dev = get_array_from_vset(dev->ioa, dev); if (!dev->array_rcd->resync_cand) { fprintf(stderr, "%s is not a candidate for checking.\n", args[0]); return -EINVAL; } dev->array_rcd->issue_cmd = 1; return ipr_resync_array(dev->ioa); } /** * start_ioa_cache - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int start_ioa_cache(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); struct ipr_reclaim_query_data reclaim_buffer; int rc; if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } rc = ipr_reclaim_cache_store(dev->ioa, IPR_RECLAIM_RESET_BATTERY_ERROR | IPR_RECLAIM_EXTENDED_INFO, &reclaim_buffer); if (rc) return rc; if (reclaim_buffer.action_status != IPR_ACTION_SUCCESSFUL) rc = -EIO; return rc; } /** * force_cache_battery_error - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int force_cache_battery_error(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); struct ipr_reclaim_query_data reclaim_buffer; if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } return ipr_reclaim_cache_store(dev->ioa, IPR_RECLAIM_FORCE_BATTERY_ERROR, &reclaim_buffer); } /** * set_bus_width - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_bus_width(char **args, int num_args) { int rc; struct ipr_scsi_buses page_28_cur; struct ipr_scsi_buses page_28_chg; struct ipr_ioa *ioa; struct ipr_dev *dev = find_dev(args[0]); int bus = strtoul(args[1], NULL, 10); int width = strtoul(args[2], NULL, 10); if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (!ioa_is_spi(dev->ioa) || dev->ioa->is_aux_cache) return -EINVAL; ioa = dev->ioa; memset(&page_28_cur, 0, sizeof(page_28_cur)); memset(&page_28_chg, 0, sizeof(page_28_chg)); rc = ipr_get_bus_attr(ioa, &page_28_cur); if (rc) return rc; if (bus >= page_28_cur.num_buses) { fprintf(stderr, "Invalid bus specified: %d\n", bus); return -EINVAL; } get_changeable_bus_attr(ioa, &page_28_chg, page_28_cur.num_buses); if (!page_28_chg.bus[bus].bus_width) { fprintf(stderr, "Bus width not changeable for this device\n"); return -EINVAL; } page_28_cur.bus[bus].bus_width = width; return ipr_set_bus_attr(ioa, &page_28_cur, 1); } /** * set_bus_speed - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_bus_speed(char **args, int num_args) { int rc, max_speed, new_xfer_rate; struct ipr_scsi_buses page_28_cur; struct ipr_scsi_buses page_28_chg; struct ipr_ioa *ioa; struct ipr_dev *dev = find_dev(args[0]); int bus = strtoul(args[1], NULL, 10); int speed = strtoul(args[2], NULL, 10); if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (!ioa_is_spi(dev->ioa) || dev->ioa->is_aux_cache) return -EINVAL; ioa = dev->ioa; memset(&page_28_cur, 0, sizeof(page_28_cur)); memset(&page_28_chg, 0, sizeof(page_28_chg)); rc = ipr_get_bus_attr(ioa, &page_28_cur); if (rc) return rc; if (bus >= page_28_cur.num_buses) { fprintf(stderr, "Invalid bus specified: %d\n", bus); return -EINVAL; } get_changeable_bus_attr(ioa, &page_28_chg, page_28_cur.num_buses); max_speed = get_max_bus_speed(ioa, bus); new_xfer_rate = IPR_BUS_THRUPUT_TO_XFER_RATE(speed, page_28_cur.bus[bus].bus_width); if (speed > max_speed) { fprintf(stderr, "Max speed allowed: %d MB/sec\n", max_speed); return -EINVAL; } page_28_cur.bus[bus].max_xfer_rate = new_xfer_rate; return ipr_set_bus_attr(ioa, &page_28_cur, 1); } /** * set_initiator_id - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_initiator_id(char **args, int num_args) { int rc; struct ipr_scsi_buses page_28_cur; struct ipr_scsi_buses page_28_chg; struct ipr_ioa *ioa; struct ipr_dev *dev = find_dev(args[0]); int bus = strtoul(args[1], NULL, 10); int scsi_id = strtoul(args[2], NULL, 10); if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (!ioa_is_spi(dev->ioa) || dev->ioa->is_aux_cache) return -EINVAL; if (scsi_id > 7) { fprintf(stderr, "Host scsi id must be < 7\n"); return -EINVAL; } ioa = dev->ioa; memset(&page_28_cur, 0, sizeof(page_28_cur)); memset(&page_28_chg, 0, sizeof(page_28_chg)); rc = ipr_get_bus_attr(ioa, &page_28_cur); if (rc) return rc; if (bus >= page_28_cur.num_buses) { fprintf(stderr, "Invalid bus specified: %d\n", bus); return -EINVAL; } get_changeable_bus_attr(ioa, &page_28_chg, page_28_cur.num_buses); if (!page_28_chg.bus[bus].scsi_id) { fprintf(stderr, "SCSI ID is not changeable for this adapter.\n"); return -EINVAL; } for_each_dev(ioa, dev) { if (!dev->scsi_dev_data) continue; if (scsi_id == dev->scsi_dev_data->id && bus == dev->scsi_dev_data->channel) { fprintf(stderr, "SCSI ID %d conflicts with a device\n", scsi_id); return -EINVAL; } } page_28_cur.bus[bus].scsi_id = scsi_id; return ipr_set_bus_attr(ioa, &page_28_cur, 1); } /** * find_slot - * @devs: ipr_dev struct * @num_devs: number of devices * @slot: slot description * * Returns: * ipr_dev struct on success / NULL otherwise **/ static struct ipr_dev *find_slot(struct ipr_dev **devs, int num_devs, char *slot) { int i; for (i = 0; i < num_devs; i++) { syslog_dbg("Looking for slot %s at pos %d\n", slot, i); if (!strncmp(devs[i]->physical_location, slot, strlen(slot))) return devs[i]; } return NULL; } /** * __add_device - * @dev: ipr dev struct * @on: * * Returns: * 0 if success / non-zero on failure **/ static int __add_device(struct ipr_dev *dev, int on) { struct ipr_dev *ses = dev->ses[0]; struct ipr_encl_status_ctl_pg ses_data; struct ipr_drive_elem_status *elem_status, *overall; struct ipr_ses_config_pg ses_cfg; int rc; if (!ses) { fprintf(stderr, "Invalid device\n"); return -EINVAL; } if (dev->ioa->sis64) elem_status = get_elem_status_64bit(dev, ses, &ses_data, &ses_cfg); else elem_status = get_elem_status(dev, ses, &ses_data, &ses_cfg); if (!elem_status) { scsi_err(dev, "Cannot find SES device for specified device\n"); return -EINVAL; } elem_status->select = 1; elem_status->remove = 0; elem_status->insert = on; elem_status->identify = on; overall = ipr_get_overall_elem(&ses_data, &ses_cfg); overall->select = 1; overall->disable_resets = on; rc = ipr_send_diagnostics(ses, &ses_data, ntohs(ses_data.byte_count) + 4); if (!on) { if (dev->ioa->sis64) wait_for_new_dev_64bit(dev->ioa, &(dev->res_path[0])); else wait_for_new_dev(dev->ioa, &(dev->res_addr[0])); } return rc; } /** * __remove_device - * @dev: ipr dev struct * @on: * * Returns: * 0 if success / non-zero on failure **/ static int __remove_device(struct ipr_dev *dev, int on) { struct ipr_dev *ses = dev->ses[0]; struct ipr_encl_status_ctl_pg ses_data; struct ipr_drive_elem_status *elem_status, *overall; struct ipr_ses_config_pg ses_cfg; int rc; if (!dev->ses) { fprintf(stderr, "Invalid device specified\n"); return -EINVAL; } if (!on && ipr_is_af_dasd_device(dev)) { if (!ipr_can_remove_device(dev)) { scsi_err(dev, "Cannot remove device\n"); return -EINVAL; } } if (dev->ioa->sis64) elem_status = get_elem_status_64bit(dev, ses, &ses_data, &ses_cfg); else elem_status = get_elem_status(dev, ses, &ses_data, &ses_cfg); if (!elem_status) { scsi_err(dev, "Invalid device specified\n"); return -EINVAL; } elem_status->select = 1; elem_status->remove = on; elem_status->insert = 0; elem_status->identify = on; overall = ipr_get_overall_elem(&ses_data, &ses_cfg); overall->select = 1; overall->disable_resets = on; rc = ipr_send_diagnostics(ses, &ses_data, ntohs(ses_data.byte_count) + 4); if (!on) { if (dev->ioa->sis64) { if (!remove_or_add_back_device_64bit(dev)) { printf(_("Disk is not removed physically, Add it back automically\n")); sleep(5); } } else { ipr_write_dev_attr(dev, "delete", "1"); evaluate_device(dev, dev->ioa, 0); ipr_del_zeroed_dev(dev); } } return rc; } /** * add_slot - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int add_slot(char **args, int num_args) { int num_devs, rc; struct ipr_dev *dev, **devs; int on = strtoul(args[1], NULL, 10); num_devs = get_conc_devs(&devs, IPR_CONC_ADD); dev = find_slot(devs, num_devs, args[0]); if (!dev) { fprintf(stderr, "Invalid location %s\n", args[0]); return -EINVAL; } rc = __add_device(dev, on); free_empty_slots(devs, num_devs); free(devs); return rc; } /** * remove_slot - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int remove_slot(char **args, int num_args) { int num_devs, rc; struct ipr_dev *dev, **devs; int on = strtoul(args[1], NULL, 10); num_devs = get_conc_devs(&devs, IPR_CONC_REMOVE); dev = find_slot(devs, num_devs, args[0]); if (!dev) { fprintf(stderr, "Invalid location %s\n", args[0]); return -EINVAL; } rc = __remove_device(dev, on); free_empty_slots(devs, num_devs); free(devs); return rc; } /** * remove_disk - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int remove_disk(char **args, int num_args) { int num_devs, i; struct ipr_dev **devs; struct ipr_dev *dev = find_dev(args[0]); int on = strtoul(args[1], NULL, 10); int rc = -EINVAL; if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } num_devs = get_conc_devs(&devs, IPR_CONC_REMOVE); for (i = 0; i < num_devs; i++) { if (devs[i] == dev) { rc = __remove_device(dev, on); break; } } free_empty_slots(devs, num_devs); free(devs); return rc; } /** * __identify_device - * @dev: ipr dev struct * @on: * * Returns: * 0 if success / non-zero on failure **/ static int __identify_device(struct ipr_dev *dev, int on) { struct ipr_dev *ses = dev->ses[0]; struct ipr_encl_status_ctl_pg ses_data; struct ipr_drive_elem_status *elem_status; struct ipr_ses_config_pg ses_cfg; if (!dev->ses) { fprintf(stderr, "Invalid device specified\n"); return -EINVAL; } if (dev->ioa->sis64) elem_status = get_elem_status_64bit(dev, ses, &ses_data, &ses_cfg); else elem_status = get_elem_status(dev, ses, &ses_data, &ses_cfg); if (!elem_status) { scsi_err(dev, "Invalid device specified\n"); return -EINVAL; } elem_status->select = 1; elem_status->identify = on; elem_status->insert = 0; elem_status->remove = 0; return ipr_send_diagnostics(ses, &ses_data, ntohs(ses_data.byte_count) + 4); } /** * identify_slot - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int identify_slot(char **args, int num_args) { int num_devs, rc; struct ipr_dev *dev, **devs; int on = strtoul(args[1], NULL, 10); num_devs = get_conc_devs(&devs, IPR_CONC_IDENTIFY); dev = find_slot(devs, num_devs, args[0]); if (!dev) { fprintf(stderr, "Invalid location %s\n", args[0]); return -EINVAL; } rc = __identify_device(dev, on); free_empty_slots(devs, num_devs); free(devs); return rc; } /** * identify_disk - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int identify_disk(char **args, int num_args) { int num_devs, i; struct ipr_dev **devs; struct ipr_dev *dev = find_dev(args[0]); int rc = -EINVAL; if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } num_devs = get_conc_devs(&devs, IPR_CONC_IDENTIFY); for (i = 0; i < num_devs; i++) { if (devs[i] == dev) { rc = __identify_device(dev, strtoul(args[1], NULL, 10)); break; } } free_empty_slots(devs, num_devs); free(devs); return rc; } /** * set_log_level_cmd - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_log_level_cmd(char **args, int num_args) { char buf[4]; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (&dev->ioa->ioa != dev) { fprintf(stderr, "Device is not an IOA\n"); return -EINVAL; } snprintf(buf, sizeof(buf), "%s\n", args[1]); set_log_level(dev->ioa, buf); return 0; } /** * set_tcq_enable - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_tcq_enable(char **args, int num_args) { int rc; struct ipr_disk_attr attr; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } rc = ipr_get_dev_attr(dev, &attr); if (rc) return rc; rc = ipr_modify_dev_attr(dev, &attr); if (rc) return rc; attr.tcq_enabled = strtoul(args[1], NULL, 10); if (attr.tcq_enabled != 0 && attr.tcq_enabled != 1) { fprintf(stderr, "Invalid parameter\n"); return -EINVAL; } return ipr_set_dev_attr(dev, &attr, 1); } /** * set_qdepth - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_qdepth(char **args, int num_args) { int rc; struct ipr_disk_attr attr; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } rc = ipr_get_dev_attr(dev, &attr); if (rc) return rc; rc = ipr_modify_dev_attr(dev, &attr); if (rc) return rc; attr.queue_depth = strtoul(args[1], NULL, 10); if (attr.queue_depth > 255) { fprintf(stderr, "Invalid queue depth %s\n", args[1]); return -EINVAL; } return ipr_set_dev_attr(dev, &attr, 1); } /** * set_format_timeout - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_format_timeout(char **args, int num_args) { int rc; struct ipr_disk_attr attr; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } rc = ipr_get_dev_attr(dev, &attr); if (rc) return rc; rc = ipr_modify_dev_attr(dev, &attr); if (rc) return rc; attr.format_timeout = strtoul(args[1], NULL, 10) * 3600; return ipr_set_dev_attr(dev, &attr, 1); } /** * update_ioa_ucode - * @ioa: ipr ioa struct * @file: file name * * Returns: * 0 if success / non-zero on failure **/ static int update_ioa_ucode(struct ipr_ioa *ioa, char *file) { struct ipr_fw_images image; int rc; strcpy(image.file, file); image.version = get_ioa_ucode_version(file); image.has_header = 0; rc = ipr_update_ioa_fw(ioa, &image, 1); if (image.version != get_ioa_fw_version(ioa)) return -EIO; return rc; } /** * update_dev_ucode - * @dev: ipr dev struct * @file: file name * * Returns: * 0 if success / non-zero on failure **/ static int update_dev_ucode(struct ipr_dev *dev, char *file) { struct ipr_fw_images image; int rc; strcpy(image.file, file); if (dev->scsi_dev_data->type == TYPE_ENCLOSURE || dev->scsi_dev_data->type == TYPE_PROCESSOR) { image.version = get_ses_ucode_version(file); } else { image.version = get_dasd_ucode_version(file, 1); image.has_header = 1; } rc = ipr_update_disk_fw(dev, &image, 1); if (image.version != get_dev_fw_version(dev)) return -EIO; return rc; } /** * update_ucode_cmd - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int update_ucode_cmd(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (&dev->ioa->ioa == dev) return update_ioa_ucode(dev->ioa, args[1]); else return update_dev_ucode(dev, args[1]); } /** * disrupt_device_cmd - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int disrupt_device_cmd(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (!ipr_is_af_dasd_device(dev)) { fprintf(stderr, "%s is not an Advanced Function disk\n", args[0]); return -EINVAL; } return ipr_disrupt_device(dev); } /** * raid_rebuild_cmd - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int raid_rebuild_cmd(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } enable_af(dev); dev->dev_rcd->issue_cmd = 1; return ipr_rebuild_device_data(dev->ioa); } /** * __reclaim - Reclaim the specified IOA's write cache. * @args: argument vector * @num_args: number of arguments * @action: * * Returns: * 0 if success / non-zero on failure **/ static int __reclaim(char **args, int num_args, int action) { struct ipr_reclaim_query_data buf; struct ipr_dev *dev; struct ipr_ioa *ioa; int rc; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } ioa = dev->ioa; rc = ipr_reclaim_cache_store(ioa, action, &buf); if (rc != 0) return rc; memset(&buf, 0, sizeof(buf)); rc = ipr_reclaim_cache_store(ioa, IPR_RECLAIM_QUERY, &buf); if (rc != 0) return rc; print_reclaim_results(&buf); return 0; } /** * reclaim - Reclaim the specified IOA's write cache. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int reclaim(char **args, int num_args) { return __reclaim(args, num_args, IPR_RECLAIM_PERFORM); } /** * reclaim_unknown - Reclaim the specified IOA's write cache and allow * unknown data loss. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int reclaim_unknown(char **args, int num_args) { return __reclaim(args, num_args, IPR_RECLAIM_PERFORM | IPR_RECLAIM_UNKNOWN_PERM); } /** * query_path_details - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_path_details(char **args, int num_args) { char *buf; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } buf = print_path_details(dev, NULL); if (buf) { printf("%s\n%s\n", status_hdr[6], status_sep[6]); printf("%s", buf); free(buf); } return 0; } /** * printf_path_status - * @dev: ipr dev struct * * Returns: * nothing **/ static void printf_path_status(struct ipr_dev *dev) { char *buf = __print_device(dev, NULL, NULL, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0); if (buf) { printf("%s", buf); free(buf); } } /** * printf_ioa_path_status - * @ioa: ipr ioa struct * * Returns: * 0 **/ static int printf_ioa_path_status(struct ipr_ioa *ioa) { struct ipr_dev *dev; if (__ioa_is_spi(ioa)) return 0; for_each_disk(ioa, dev) printf_path_status(dev); return 0; } /** * query_path_status - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_path_status(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *dev = NULL; int hdr = 1; if (!num_args) { for_each_sas_ioa(ioa) { if (hdr) { printf("%s\n%s\n", status_hdr[2], status_sep[2]); hdr = 0; } printf_ioa_path_status(ioa); } return 0; } dev = find_dev(args[0]); if (!dev) return -ENXIO; printf("%s\n%s\n", status_hdr[2], status_sep[2]); if (&dev->ioa->ioa == dev) printf_ioa_path_status(dev->ioa); else printf_path_status(dev); return 0; } /** * query_recommended_stripe_size - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_recommended_stripe_size(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); struct ipr_array_cap_entry *cap; if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (dev->ioa->is_aux_cache || !dev->ioa->supported_arrays) return -EINVAL; cap = get_cap_entry(dev->ioa->supported_arrays, args[1]); if (cap) printf("%d\n", ntohs(cap->recommended_stripe_size)); return 0; } /** * query_supp_stripe_sizes - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_supp_stripe_sizes(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); struct ipr_array_cap_entry *cap; int i; if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (dev->ioa->is_aux_cache || !dev->ioa->supported_arrays) return -EINVAL; cap = get_cap_entry(dev->ioa->supported_arrays, args[1]); if (!cap) return 0; for (i = 0; i < (sizeof(cap->supported_stripe_sizes) * 8); i++) if (ntohs(cap->supported_stripe_sizes) & (1 << i)) printf("%d ", (1 << i)); printf("\n"); return 0; } /** * query_min_mult_in_array - Start array protection for an array * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_min_mult_in_array(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); struct ipr_array_cap_entry *cap; if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (dev->ioa->is_aux_cache || !dev->ioa->supported_arrays) return -EINVAL; cap = get_cap_entry(dev->ioa->supported_arrays, args[1]); if (cap) printf("%d\n", cap->min_mult_array_devices); return 0; } /** * query_min_array_devices - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_min_array_devices(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); struct ipr_array_cap_entry *cap; if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (dev->ioa->is_aux_cache || !dev->ioa->supported_arrays) return -EINVAL; cap = get_cap_entry(dev->ioa->supported_arrays, args[1]); if (cap) printf("%d\n", cap->min_num_array_devices); return 0; } /** * query_max_array_devices - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_max_array_devices(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); struct ipr_array_cap_entry *cap; if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (dev->ioa->is_aux_cache || !dev->ioa->supported_arrays) return -EINVAL; cap = get_cap_entry(dev->ioa->supported_arrays, args[1]); if (cap) printf("%d\n", cap->max_num_array_devices); return 0; } /** * query_include_allowed - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_include_allowed(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); struct ipr_array_cap_entry *cap; if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (dev->ioa->is_aux_cache || !dev->ioa->supported_arrays) { printf("no\n"); return 0; } cap = get_cap_entry(dev->ioa->supported_arrays, args[1]); if (!cap) { printf("no\n"); return 0; } if (cap->include_allowed) printf("yes\n"); else printf("no\n"); return 0; } /** * query_raid_levels - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_raid_levels(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); struct ipr_array_cap_entry *cap; if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (dev->ioa->is_aux_cache || !dev->ioa->supported_arrays) return 0; for_each_cap_entry(cap, dev->ioa->supported_arrays) printf("%s ", cap->prot_level_str); printf("\n"); return 0; } /** * query_bus_width - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_bus_width(char **args, int num_args) { int rc, bus; struct ipr_scsi_buses page_28; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (!ioa_is_spi(dev->ioa) || dev->ioa->is_aux_cache) return -EINVAL; memset(&page_28, 0, sizeof(struct ipr_scsi_buses)); rc = ipr_get_bus_attr(dev->ioa, &page_28); if (rc) return rc; bus = strtoul(args[1], NULL, 10); if (bus >= page_28.num_buses) { fprintf(stderr, "Invalid bus specified: %d\n", bus); return -EINVAL; } fprintf(stdout, "%d\n", page_28.bus[bus].bus_width); return 0; } /** * query_bus_speed - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_bus_speed(char **args, int num_args) { int rc, bus; struct ipr_scsi_buses page_28; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (!ioa_is_spi(dev->ioa) || dev->ioa->is_aux_cache) return -EINVAL; memset(&page_28, 0, sizeof(struct ipr_scsi_buses)); rc = ipr_get_bus_attr(dev->ioa, &page_28); if (rc) return rc; bus = strtoul(args[1], NULL, 10); if (bus >= page_28.num_buses) { fprintf(stderr, "Invalid bus specified: %d\n", bus); return -EINVAL; } fprintf(stdout, "%d\n", IPR_BUS_XFER_RATE_TO_THRUPUT(page_28.bus[bus].max_xfer_rate, page_28.bus[bus].bus_width)); return 0; } /** * query_initiator_id - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_initiator_id(char **args, int num_args) { int rc, bus; struct ipr_scsi_buses page_28; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Invalid device %s\n", args[0]); return -EINVAL; } if (!ioa_is_spi(dev->ioa) || dev->ioa->is_aux_cache) return -EINVAL; memset(&page_28, 0, sizeof(struct ipr_scsi_buses)); rc = ipr_get_bus_attr(dev->ioa, &page_28); if (rc) return rc; bus = strtoul(args[1], NULL, 10); if (bus >= page_28.num_buses) { fprintf(stderr, "Invalid bus specified: %d\n", bus); return -EINVAL; } fprintf(stdout, "%d\n", page_28.bus[bus].scsi_id); return 0; } /** * query_add_remove - * @action: action type * * Returns: * 0 **/ static int query_add_remove(int action) { int i, num_devs; struct ipr_dev **devs; num_devs = get_conc_devs(&devs, action); for (i = 0; i < num_devs; i++) { if (i == 0) printf("%s\n%s\n", status_hdr[11], status_sep[11]); printf_device(devs[i], 5); } free_empty_slots(devs, num_devs); free(devs); return 0; } /** * query_remove_device - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_remove_device(char **args, int num_args) { return query_add_remove(IPR_CONC_REMOVE); } /** * query_add_device - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_add_device(char **args, int num_args) { return query_add_remove(IPR_CONC_ADD); } /** * query_log_level - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_log_level(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (&dev->ioa->ioa != dev) { fprintf(stderr, "Device is not an IOA\n"); return -EINVAL; } printf("%d\n", get_log_level(dev->ioa)); return 0; } /** * query_tcq_enable - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_tcq_enable(char **args, int num_args) { int rc; struct ipr_disk_attr attr; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } rc = ipr_get_dev_attr(dev, &attr); if (rc) return rc; printf("%d\n", attr.tcq_enabled); return 0; } /** * query_qdepth - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_qdepth(char **args, int num_args) { int rc; struct ipr_disk_attr attr; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } rc = ipr_get_dev_attr(dev, &attr); if (rc) return rc; printf("%d\n", attr.queue_depth); return 0; } /** * query_format_timeout - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_format_timeout(char **args, int num_args) { int rc; struct ipr_disk_attr attr; struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (!ipr_is_af_dasd_device(dev)) { dprintf("%s is not an Advanced Function disk\n", args[0]); return -EINVAL; } rc = ipr_get_dev_attr(dev, &attr); if (rc) return rc; printf("%d\n", attr.format_timeout / 3600); return 0; } /** * query_ucode_level - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_ucode_level(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); u32 level, level_sw; char *asc; if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (&dev->ioa->ioa == dev) printf("%08X\n", get_ioa_fw_version(dev->ioa)); else { level = get_dev_fw_version(dev); level_sw = htonl(level); asc = (char *)&level_sw; if (isprint(asc[0]) && isprint(asc[1]) && isprint(asc[2]) && isprint(asc[3])) printf("%.8X (%c%c%c%c)\n", level, asc[0], asc[1], asc[2], asc[3]); else printf("%.8X\n", level); } return 0; } /** * query_format_for_raid - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_format_for_raid(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *dev; int hdr = 0; for_each_primary_ioa(ioa) { for_each_disk(ioa, dev) { if (!can_format_for_raid(dev)) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } if (strlen(dev->dev_name)) printf_device(dev, 0); else printf_device(dev, 2); } } return 0; } /** * query_raid_rebuild - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_raid_rebuild(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *dev; int hdr = 0; for_each_ioa(ioa) { for_each_af_dasd(ioa, dev) { if (!dev->dev_rcd->rebuild_cand) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(dev, 2); } } return 0; } /** * query_recovery_format - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_recovery_format(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *dev; int hdr = 0; for_each_primary_ioa(ioa) { for_each_disk(ioa, dev) { if (ipr_is_hot_spare(dev) || !device_supported(dev)) continue; if (ipr_is_array_member(dev) && !dev->dev_rcd->no_cfgte_vol) continue;; if (!is_format_allowed(dev)) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } if (strlen(dev->dev_name)) printf_device(dev, 0); else printf_device(dev, 2); } } return 0; } /** * query_devices_include - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_devices_include(char **args, int num_args) { int rc, hdr = 0;; struct ipr_array_query_data qac_data; struct ipr_dev *dev; struct ipr_ioa *ioa; struct ipr_dev *vset = find_dev(args[0]); struct ipr_dev_record *dev_rcd; if (!vset) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } ioa = vset->ioa; memset(&qac_data, 0, sizeof(qac_data)); rc = ipr_query_array_config(ioa, 0, 1, 0, vset->array_id, &qac_data); if (rc) return rc; for_each_dev_rcd(dev_rcd, &qac_data) { for_each_disk(ioa, dev) { if (dev->scsi_dev_data->handle == ipr_get_dev_res_handle(ioa, dev_rcd) && dev_rcd->include_cand && device_supported(dev)) { if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(dev, 2); break; } } } return 0; } /** * query_arrays_include - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_arrays_include(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *vset; struct ipr_array_cap_entry *cap_entry; struct ipr_array_record *array_rcd; int hdr = 0; for_each_primary_ioa(ioa) { for_each_vset(ioa, vset) { array_rcd = vset->array_rcd; cap_entry = get_raid_cap_entry(ioa->supported_arrays, vset->raid_level); if (!cap_entry || !cap_entry->include_allowed || !array_rcd->established) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[3], status_sep[3]); } printf_device(vset, 1); } } return 0; } /** * query_reclaim - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_reclaim(char **args, int num_args) { struct ipr_reclaim_query_data buf; struct ipr_ioa *ioa; int hdr = 0, rc; for_each_ioa(ioa) { memset(&buf, 0, sizeof(buf)); rc = ipr_reclaim_cache_store(ioa, IPR_RECLAIM_QUERY, &buf); if (rc != 0) continue; if (buf.reclaim_known_needed || buf.reclaim_unknown_needed) { if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(&ioa->ioa, 2); } } return 0; } /** * query_arrays_raid_migrate - Show the arrays that can be migrated to a * different protection level. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_arrays_raid_migrate(char **args, int num_args) { int hdr = 0; struct ipr_dev *array, *vset; struct ipr_ioa *ioa; for_each_primary_ioa(ioa) { for_each_array(ioa, array) { if (!array->array_rcd->migrate_cand) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[3], status_sep[3]); } if (ioa->sis64) { vset = get_vset_from_array(ioa, array); printf_device(vset, 1); } else printf_device(array, 1); } } return 0; } /** * query_devices_raid_migrate - Given an array, show the AF disks that are * candidates to be used in a migration. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_devices_raid_migrate(char **args, int num_args) { struct ipr_dev *dev; struct ipr_dev_record *dev_rcd; int hdr = 0; int rc = 0; struct ipr_array_query_data qac_data; memset(&qac_data, 0, sizeof(qac_data)); dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (dev->ioa->sis64 && ipr_is_volume_set(dev)) dev = get_array_from_vset(dev->ioa, dev); if (!ipr_is_array(dev)) { fprintf(stderr, "%s is not an array.\n", args[0]); return -EINVAL; } /* query array config for volume set migrate status */ rc = ipr_query_array_config(dev->ioa, 0, 0, 1, dev->array_id, &qac_data); if (rc != 0) return rc; for_each_dev_rcd(dev_rcd, &qac_data) { if (!dev_rcd->migrate_array_prot_cand) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } dev = get_dev_from_handle(dev->ioa, ipr_get_dev_res_handle(dev->ioa, dev_rcd)); if (dev) printf_device(dev, 2); } return 0; } /** * query_raid_levels_raid_migrate - Given an array, display the protection * levels to which the array can be migrated. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_raid_levels_raid_migrate(char **args, int num_args) { struct ipr_dev *dev; int rc = 0; struct ipr_array_query_data qac_data; struct ipr_supported_arrays *sa_rcd; struct ipr_array_cap_entry *cap_entry; memset(&qac_data, 0, sizeof(qac_data)); dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (dev->ioa->sis64 && ipr_is_volume_set(dev)) dev = get_array_from_vset(dev->ioa, dev); if (!ipr_is_array(dev)) { fprintf(stderr, "%s is not an array.\n", args[0]); return -EINVAL; } /* query array config for volume set migrate status */ rc = ipr_query_array_config(dev->ioa, 0, 0, 1, dev->array_id, &qac_data); if (rc) return rc; for_each_supported_arrays_rcd(sa_rcd, &qac_data) for_each_cap_entry(cap_entry, sa_rcd) printf("%s ", cap_entry->prot_level_str); printf("\n"); return 0; } /** * query_stripe_sizes_raid_migrate - Given an array and a protection level, * show the valid stripe sizes to which the * array can be migrated. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_stripe_sizes_raid_migrate(char **args, int num_args) { struct ipr_dev *dev; int i; int rc = 0; struct ipr_array_query_data qac_data; struct ipr_supported_arrays *sa_rcd; struct ipr_array_cap_entry *cap_entry; memset(&qac_data, 0, sizeof(qac_data)); dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (dev->ioa->sis64 && ipr_is_volume_set(dev)) dev = get_array_from_vset(dev->ioa, dev); if (!ipr_is_array(dev)) { fprintf(stderr, "%s is not an array.\n", args[0]); return -EINVAL; } /* query array config for volume set migrate status */ rc = ipr_query_array_config(dev->ioa, 0, 0, 1, dev->array_id, &qac_data); if (rc) return rc; for_each_supported_arrays_rcd(sa_rcd, &qac_data) { for_each_cap_entry(cap_entry, sa_rcd) { if (strcmp(args[1], (char *)cap_entry->prot_level_str)) continue; for (i = 0; i < (sizeof(cap_entry->supported_stripe_sizes) * 8); i++) if (ntohs(cap_entry->supported_stripe_sizes) & (1 << i)) printf("%d ", (1 << i)); printf("\n"); break; } } return 0; } /** * query_devices_min_max_raid_migrate - Given an array and a protection level, * show the minimum and maximum number of * devices needed to do the migration. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ int query_devices_min_max_raid_migrate(char **args, int num_args) { struct ipr_dev *dev; int rc = 0; struct ipr_array_query_data qac_data; struct ipr_supported_arrays *supported_arrays; struct ipr_array_cap_entry *cap; memset(&qac_data, 0, sizeof(qac_data)); dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (dev->ioa->sis64 && ipr_is_volume_set(dev)) dev = get_array_from_vset(dev->ioa, dev); if (!ipr_is_array(dev)) { fprintf(stderr, "%s is not an array.\n", args[0]); return -EINVAL; } /* query array config for volume set migrate status */ rc = ipr_query_array_config(dev->ioa, 0, 0, 1, dev->array_id, &qac_data); if (rc) return rc; /* check that the given raid level is valid */ for_each_supported_arrays_rcd(supported_arrays, &qac_data) { cap = get_cap_entry(supported_arrays, args[1]); if (cap) break; } if (!cap) { fprintf(stderr, "RAID level %s is unsupported.\n", args[1]); return -EINVAL; } if (cap->format_overlay_type == IPR_FORMAT_REMOVE_DEVICES) fprintf(stderr, "%d %s will be removed from the array\n", cap->min_num_array_devices, cap->min_num_array_devices == 1 ? "device" : "devices"); else if (cap->format_overlay_type == IPR_FORMAT_ADD_DEVICES) { fprintf(stderr, "Minimum number of devices required = %d\n", cap->min_num_array_devices); fprintf(stderr, "Maximum number of devices allowed = %d\n", cap->max_num_array_devices); fprintf(stderr, "Number of devices must be a multiple of %d\n", cap->min_mult_array_devices); } else { fprintf(stderr, "Unknown overlay type in qac data\n"); return -EINVAL; } return 0; } /** * query_ioas_asym_access - Show the ioas that support asymmetric access. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_ioas_asym_access(char **args, int num_args) { int hdr = 0; struct ipr_ioa *ioa; for_each_ioa(ioa) { if (!ioa->asymmetric_access) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[3], status_sep[3]); } printf_device(&ioa->ioa, 2); } return 0; } /** * query_arrays_asym_access - Show the arrays that support asymmetric access. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_arrays_asym_access(char **args, int num_args) { int hdr = 0; struct ipr_ioa *ioa; struct ipr_dev *array; for_each_ioa(ioa) { if (!ioa->asymmetric_access || !ioa->asymmetric_access_enabled) continue; for_each_array(ioa, array) { if (!array->array_rcd->asym_access_cand) continue; if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[3], status_sep[3]); } if (ioa->sis64) printf_device(array, 4); else printf_device(array, 1); } } return 0; } /** * query_ioa_asym_access_mode - Show the current asymmetric access state for * the given IOA. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_ioa_asym_access_mode(char **args, int num_args) { struct ipr_dev *dev; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (dev != &dev->ioa->ioa) { fprintf(stderr, "%s is not an IOA.\n", args[0]); return -EINVAL; } if (dev->ioa->asymmetric_access_enabled) printf("Enabled\n"); else if (dev->ioa->asymmetric_access) printf("Disabled\n"); else printf("Unsupported\n"); return 0; } /** * query_array_asym_access_mode - Show the current asymmetric access state for * the given array. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_array_asym_access_mode(char **args, int num_args) { struct ipr_dev *array; array = find_dev(args[0]); if (!array) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (!ipr_is_array(array)) { if (array->ioa->sis64 && ipr_is_volume_set(array)) array = get_array_from_vset(array->ioa, array); else { fprintf(stderr, "%s is not an array.\n", array->gen_name); return -EINVAL; } } if (!array->array_rcd->asym_access_cand) printf("Unsupported\n"); else if (array->array_rcd->current_asym_access_state == IPR_ACTIVE_OPTIMIZED) printf("Optimized\n"); else if (array->array_rcd->current_asym_access_state == IPR_ACTIVE_NON_OPTIMIZED) printf("Not Optimized\n"); else scsi_dbg(array, "Unrecognized state - %d.", array->array_rcd->current_asym_access_state); return 0; } /** * query_format_for_jbod - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_format_for_jbod(char **args, int num_args) { struct ipr_dev *dev; struct ipr_ioa *ioa; int hdr = 0; for_each_primary_ioa(ioa) { for_each_af_dasd(ioa, dev) { if (ipr_device_is_zeroed(dev)) continue; if ((ipr_is_array_member(dev) && dev->dev_rcd->no_cfgte_vol) || (!ipr_is_array_member(dev) && is_format_allowed(dev))) { if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(dev, 2); } } } return 0; } /** * show_dev_details - * @dev: ipr dev struct * * Returns: * nothing **/ static void show_dev_details(struct ipr_dev *dev) { char *body = NULL; if (dev->scsi_dev_data && dev->scsi_dev_data->type == IPR_TYPE_ADAPTER && dev == &dev->ioa->ioa) body = ioa_details(body, dev); else if (ipr_is_volume_set(dev) || ipr_is_array(dev)) body = vset_array_details(body, dev); else if (ipr_is_ses(dev)) body = ses_details(body, dev); else if (dev->scsi_dev_data) body = disk_details(body, dev); printf("%s\n", body); free(body); } /** * show_jbod_disks - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int show_jbod_disks(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *dev; int hdr = 0; for_each_ioa(ioa) { for_each_jbod_disk(ioa, dev) { if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(dev, 0); } } return 0; } /** * show_all_af_disks - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int show_all_af_disks(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *dev; int hdr = 0; for_each_ioa(ioa) { for_each_af_dasd(ioa, dev) { if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(dev, 2); } } return 0; } /** * show_af_disks - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int show_af_disks(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *dev; int hdr = 0; for_each_ioa(ioa) { for_each_standalone_af_disk(ioa, dev) { if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(dev, 2); } } return 0; } /** * show_hot_spares - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int show_hot_spares(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *dev; int hdr = 0; for_each_ioa(ioa) { for_each_hot_spare(ioa, dev) { if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[2], status_sep[2]); } printf_device(dev, 2); } } return 0; } /** * show_details - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int show_details(char **args, int num_args) { struct ipr_dev *dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Missing device: %s\n", args[0]); return -EINVAL; } show_dev_details(dev); return 0; } /** * __print_status - * @args: argument vector * @num_args: number of arguments * @percent: percent value * * Returns: * 0 if success / non-zero on failure **/ static int __print_status(char **args, int num_args, int percent) { struct ipr_dev *dev = find_dev(args[0]); char buf[100]; if (!dev) { printf("Missing\n"); return -EINVAL; } get_status(dev, buf, percent, 0); printf("%s\n", buf); return 0; } /** * print_alt_status - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int print_alt_status(char **args, int num_args) { return __print_status(args, num_args, 1); } /** * print_status - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int print_status(char **args, int num_args) { return __print_status(args, num_args, 0); } /** * __show_config - * @type: type value * * Returns: * 0 if success / non-zero on failure **/ static int __show_config(int type) { struct ipr_ioa *ioa; printf("%s\n%s\n", status_hdr[(type&1)+2], status_sep[(type&1)+2]); for_each_ioa(ioa) printf_ioa(ioa, type); return 0; } /** * show_config - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int show_config(char **args, int num_args) { return __show_config(3); } /** * show_alt_config - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int show_alt_config(char **args, int num_args) { return __show_config(2); } /** * show_ioas - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int show_ioas(char **args, int num_args) { struct ipr_ioa *ioa; if (num_ioas) printf("%s\n%s\n", status_hdr[2], status_sep[2]); else printf("No IOAs found\n"); for_each_ioa(ioa) printf_device(&ioa->ioa, 2); return 0; } /** * show_arrays - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int show_arrays(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *vset; int hdr = 0; for_each_ioa(ioa) { for_each_vset(ioa, vset) { if (!hdr) { hdr = 1; printf("%s\n%s\n", status_hdr[3], status_sep[3]); } printf_device(vset, 3); } } return 0; } /** * battery_info - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int battery_info(char **args, int num_args) { struct ipr_reclaim_query_data reclaim; struct ipr_dev *dev; char *buf; int rc; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } rc = ipr_reclaim_cache_store(dev->ioa, IPR_RECLAIM_QUERY | IPR_RECLAIM_EXTENDED_INFO, &reclaim); if (rc) return rc; dev->ioa->reclaim_data = &reclaim; buf = get_battery_info(dev->ioa); printf("%s\n", buf); dev->ioa->reclaim_data = NULL; free(buf); return 0; } /** * get_ha_mode - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int get_ha_mode(char **args, int num_args) { struct ipr_dev *dev = find_gen_dev(args[0]); if (!dev) return -ENXIO; if (dev->ioa->in_gscsi_only_ha) printf("JBOD\n"); else printf("Normal\n"); return 0; } /** * __set_ha_mode - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int __set_ha_mode(char **args, int num_args) { struct ipr_dev *dev = find_gen_dev(args[0]); if (!dev) return -ENXIO; if (!strncasecmp(args[1], "Normal", 4)) return set_ha_mode(dev->ioa, 0); else if (!strncasecmp(args[1], "JBOD", 4)) return set_ha_mode(dev->ioa, 1); return -EINVAL; } /** * __set_preferred_primary - * @sg_name: sg device name * @preferred_primary: preferred primary value * * Returns: * 0 if success / non-zero on failure **/ static int __set_preferred_primary(char *sg_name, int preferred_primary) { struct ipr_dev *dev = find_gen_dev(sg_name); if (dev) return set_preferred_primary(dev->ioa, preferred_primary); return -ENXIO; } /** * set_primary - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_primary(char **args, int num_args) { return __set_preferred_primary(args[0], 1); } /** * set_secondary - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_secondary(char **args, int num_args) { return __set_preferred_primary(args[0], 0); } /** * set_all_primary - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_all_primary(char **args, int num_args) { struct ipr_ioa *ioa; for_each_ioa(ioa) set_preferred_primary(ioa, 1); return 0; } /** * set_all_secondary - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_all_secondary(char **args, int num_args) { struct ipr_ioa *ioa; for_each_ioa(ioa) set_preferred_primary(ioa, 0); return 0; } /** * check_and_set_active_active - Do some sanity checks and then set the * active/active state to the requested mode. * @dev_name: specified IOA * @mode: mode to set - enable or disable * * Returns: * 0 if success / non-zero on failure **/ static int check_and_set_active_active(char *dev_name, int mode) { struct ipr_dev *dev; dev = find_dev(dev_name); if (!dev) { fprintf(stderr, "Cannot find %s\n", dev_name); return -EINVAL; } if (dev != &dev->ioa->ioa) { fprintf(stderr, "%s is not an IOA.\n", dev_name); return -EINVAL; } /* Check that asymmetric access is supported by the adapter. */ if (!dev->ioa->asymmetric_access) { ioa_err(dev->ioa, "IOA does not support asymmetric access.\n"); return -EINVAL; } /* Check the state of asymmetric access. */ if (dev->ioa->asymmetric_access_enabled == mode) { ioa_dbg(dev->ioa, "Asymmetric access is already %s.\n", mode == 0 ? "disabled" : "enabled"); return 0; } return set_active_active_mode(dev->ioa, mode); } /** * get_drive_phy_loc - get drive physical location * @ioa: ipr ioa struct * * Returns: * body **/ static int get_drive_phy_loc(struct ipr_ioa *ioa) { struct ipr_encl_status_ctl_pg ses_data; struct ipr_drive_elem_status *elem_status, *overall; struct ipr_dev *ses, *dev; struct ipr_ses_config_pg ses_cfg; int ses_bus, scsi_id_found, is_spi, is_vses; struct drive_elem_desc_pg drive_data; char phy_loc[PHYSICAL_LOCATION_LENGTH + 1]; int times, index; for_each_ioa(ioa) { is_spi = ioa_is_spi(ioa); for_each_hotplug_dev(ioa, dev) { if (ioa->sis64) get_res_path(dev); else get_res_addrs(dev); } for_each_ses(ioa, ses) { times = 5; if (strlen(ses->physical_location) == 0) get_ses_phy_loc(ses); while (times--) { if (!ipr_receive_diagnostics(ses, 2, &ses_data, sizeof(ses_data))) break; } if (times < 0 ) continue; if (ipr_receive_diagnostics(ses, 1, &ses_cfg, sizeof(ses_cfg))) continue; if (ipr_receive_diagnostics(ses, 7, &drive_data, sizeof(drive_data))) continue; overall = ipr_get_overall_elem(&ses_data, &ses_cfg); ses_bus = ses->scsi_dev_data->channel; scsi_id_found = 0; if (!is_spi && (overall->device_environment == 0)) is_vses = 1; else is_vses = 0; scsi_dbg(ses, "%s\n", is_vses ? "Found VSES" : "Found real SES"); for_each_elem_status(elem_status, &ses_data, &ses_cfg) { index = index_in_page2(&ses_data, elem_status->slot_id); if (index != -1) get_drive_phy_loc_with_ses_phy_loc(ses, &drive_data, index, phy_loc, 0); if (elem_status->status == IPR_DRIVE_ELEM_STATUS_UNSUPP) continue; if (elem_status->status == IPR_DRIVE_ELEM_STATUS_NO_ACCESS) continue; if (is_spi && (scsi_id_found & (1 << elem_status->slot_id))) continue; scsi_id_found |= (1 << elem_status->slot_id); if (ioa->sis64) dev = get_dev_for_slot_64bit(ses, elem_status->slot_id, phy_loc); else dev = get_dev_for_slot(ses, elem_status->slot_id, is_vses, phy_loc); if (!dev) continue; } } } return 0; } /** * set_ioa_asymmetric_access - Change the asymmetric access mode for an IOA. * Enabling occurs by sending a change multi adapter * assignment command from ipr_set_ioa_attr(). * Disabling occurs by resetting the adapter and * then sending a mode select * page 24 in ipr_set_active_active_mode(). * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_ioa_asymmetric_access(char **args, int num_args) { if (!strncasecmp(args[1], "Enable", 4)) return check_and_set_active_active(args[0], 1); else if (!strncasecmp(args[1], "Disable", 4)) return check_and_set_active_active(args[0], 0); return -EINVAL; } /** * set_array_asymmetric_access - Set the desired asymmetric access state of the * array to "Optimized" or "Non-Optimized". * * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_array_asymmetric_access(char **args, int num_args) { int state; struct ipr_dev *array = NULL; array = find_dev(args[0]); if (!array) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (!ipr_is_array(array)) { if (array->ioa->sis64 && ipr_is_volume_set(array)) array = get_array_from_vset(array->ioa, array); else { scsi_err(array, "Given device is not an array."); return -EINVAL; } } /* Check that the adapter is a primary adapter. */ if (array->ioa->is_secondary) { ioa_err(array->ioa, "Asymmetric access commands must be issued " "for arrays on the primary IOA"); return -EINVAL; } /* Check that asymmetric access is supported by the adapter. */ if (!array->ioa->asymmetric_access) { ioa_err(array->ioa, "Asymmetric access is not supported."); return -EINVAL; } /* Check that asymmetric access is enabled on the adapter. */ if (!array->ioa->asymmetric_access_enabled) { ioa_err(array->ioa, "Asymmetric access is not enabled."); return -EINVAL; } /* Optimized or Non-optimized? */ if (!strncasecmp(args[1], "Optimized", 3)) state = IPR_ACTIVE_OPTIMIZED; else if (!strncasecmp(args[1], "Non-Optimized", 3)) state = IPR_ACTIVE_NON_OPTIMIZED; else { scsi_err(array, "Unrecognized state given for asymmetric access."); return -EINVAL; } if (array->array_rcd->saved_asym_access_state == state) { scsi_dbg(array, "Array is already set to requested state."); return 0; } if (array->array_rcd->asym_access_cand) array->array_rcd->issue_cmd = 1; else { scsi_err(array, "%s is not a candidate for asymmetric access.", array->dev_name); return -EINVAL; } array->array_rcd->saved_asym_access_state = state; return ipr_set_array_asym_access(array->ioa); } /** * query_array_label - Display the device name for the specified label * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_array_label(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *vset; for_each_ioa(ioa) { if (!ioa->sis64) continue; for_each_vset(ioa, vset) { if (!strcmp((char *)vset->array_rcd->type3.desc, args[0])) { fprintf(stdout, "%s\n", vset->dev_name); return 0; } } } return -ENODEV; } /** * query_ioa_caching - Show whether or not user requested caching mode is set * to default or disabled. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_ioa_caching(char **args, int num_args) { int mode; struct ipr_dev *dev; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (dev != &dev->ioa->ioa) { fprintf(stderr, "%s is not an IOA.\n", args[0]); return -EINVAL; } if (!dev->ioa->has_cache) { printf("none\n"); return 0; } mode = get_ioa_caching(dev->ioa); switch (mode) { case IPR_IOA_REQUESTED_CACHING_DISABLED: printf("disabled\n"); break; case IPR_IOA_CACHING_DISABLED_DUAL_ENABLED: printf("disabled, 0x3\n"); break; case IPR_IOA_CACHING_DEFAULT_DUAL_ENABLED: printf("default, 0x3\n"); break; default: printf("default\n"); break; } return 0; } /** * set_ioa_caching - Set the requested IOA caching mode to default or disabled. * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_ioa_caching(char **args, int num_args) { int mode, rc; struct ipr_dev *dev; dev = find_dev(args[0]); if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (dev != &dev->ioa->ioa) { fprintf(stderr, "%s is not an IOA.\n", args[0]); return -EINVAL; } if (dev->ioa->is_aux_cache) { return -EINVAL; } if (!dev->ioa->has_cache) { fprintf(stderr, "%s does not have cache.\n", args[0]); return -EINVAL; } if (!strncasecmp(args[1], "Default", 3)) mode = IPR_IOA_SET_CACHING_DEFAULT; else if (!strncasecmp(args[1], "Disabled", 3)) mode = IPR_IOA_SET_CACHING_DISABLED; else if (!strncasecmp(args[1], "Dual-Disabled", 8)) mode = IPR_IOA_SET_CACHING_DUAL_DISABLED; else if (!strncasecmp(args[1], "Dual-Enabled", 8)) mode = IPR_IOA_SET_CACHING_DUAL_ENABLED; else { scsi_err(dev, "Unrecognized mode given for IOA caching."); return -EINVAL; } rc = ipr_change_cache_parameters(dev->ioa , mode); return rc; } /** * get_live_dump - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int get_live_dump (char **args, int num_args) { struct ipr_dev *dev = find_gen_dev(args[0]); if (dev) return ipr_get_live_dump(dev->ioa); return -ENXIO; } /** * query_ses_mode - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_ses_mode (char **args, int num_args) { struct ipr_dev *dev = find_gen_dev(args[0]); struct ipr_ses_inquiry_pageC3 pageC3_inq; int rc; if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (!ipr_is_ses(dev)) { fprintf(stderr, "Invalid device type\n"); return -EINVAL; } memset(&pageC3_inq, 0, sizeof(pageC3_inq)); rc = ipr_inquiry(dev, 0xc3, &pageC3_inq, sizeof(pageC3_inq)); if (rc != 0) { scsi_dbg(dev, "Inquiry failed\n"); return -EIO; } printf("%c%c%c%c\n", pageC3_inq.mode[0], pageC3_inq.mode[1], pageC3_inq.mode[2], pageC3_inq.mode[3]); return 0; } /** * set_ses_mode - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int set_ses_mode (char **args, int num_args) { struct ipr_dev *dev = find_gen_dev(args[0]); int mode = atoi(args[1]); int rc; if (!dev) { fprintf(stderr, "Cannot find %s\n", args[0]); return -EINVAL; } if (!ipr_is_ses(dev)) { fprintf(stderr, "Invalid device type\n"); return -EINVAL; } rc = ipr_set_ses_mode(dev, mode); if (rc != 0) { scsi_dbg(dev, "Change SES mode command failed"); return -EIO; } return 0; } /** * show_enclosure_info - * @ioa: ipr ioa struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int show_enclosure_info(struct ipr_dev *ses) { struct screen_output *s_out; int rc; if (ses->scsi_dev_data->type == TYPE_ENCLOSURE) n_show_enclosure_info.body = ses_details(n_show_enclosure_info.body, ses); else n_show_enclosure_info.body = ioa_details(n_show_enclosure_info.body, ses); s_out = screen_driver(&n_show_enclosure_info, 0, NULL); free(n_show_enclosure_info.body); n_show_enclosure_info.body = NULL; rc = s_out->rc; free(s_out); return rc; } int ipr_suspend_disk_enclosure(i_container *i_con) { int rc, suspend_rc; struct ipr_ioa *ioa; struct ipr_dev *ses; suspend_rc = rc = RC_SUCCESS; for_each_ioa(ioa) { for_each_ses(ioa, ses) { if (ses->is_suspend_cand) { rc = ipr_suspend_device_bus(ses, &ses->res_addr[0], IPR_SDB_CHECK_AND_QUIESCE_ENC); if (rc !=0 ) suspend_rc = RC_82_Suspended_Fail; else rc = ipr_write_dev_attr(ses, "state", "offline\n"); } } } if (suspend_rc != RC_SUCCESS) return suspend_rc | EXIT_FLAG; return RC_81_Suspended_Success | EXIT_FLAG; } int ipr_resume_disk_enclosure(i_container *i_con) { int rc, resume_rc; struct ipr_ioa *ioa; struct ipr_dev *ses; resume_rc = rc = RC_SUCCESS; for_each_ioa(ioa) { for_each_ses(ioa, ses) { if (ses->is_resume_cand) { rc = ipr_resume_device_bus(ses, &ses->res_addr[0]); if (rc !=0 ) resume_rc = RC_86_Enclosure_Resume_Fail; else ipr_write_dev_attr(ses, "state", "running\n"); } } } if (resume_rc != RC_SUCCESS) return resume_rc | EXIT_FLAG; return RC_85_Enclosure_Resume_Success | EXIT_FLAG; } /** * confirm_suspend_disk_enclosure - * * Returns: * 0 if success / non-zero on failure FIXME **/ int confirm_suspend_disk_enclosure(void) { int rc,k; struct ipr_ioa *ioa; struct ipr_dev *ses; char *buffer[2]; struct screen_output *s_out; int header_lines; int toggle= 0; rc = RC_SUCCESS; body_init_status_enclosure(buffer, n_confirm_suspend_disk_enclosure.header, &header_lines); for_each_ioa(ioa) { for_each_ses(ioa, ses) { if (!ses->is_suspend_cand) continue; print_dev_enclosure(k, ses, buffer, "2", k); } } do { n_confirm_suspend_disk_enclosure.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_suspend_disk_enclosure, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_suspend_disk_enclosure.body = NULL; return rc; } /** * confirm_resume_disk_enclosure - * * Returns: * 0 if success / non-zero on failure FIXME **/ int confirm_resume_disk_enclosure(void) { int rc,k; struct ipr_ioa *ioa; struct ipr_dev *ses; char *buffer[2]; struct screen_output *s_out; int header_lines; int toggle= 0; rc = RC_SUCCESS; body_init_status_enclosure(buffer, n_confirm_resume_disk_enclosure.header, &header_lines); for_each_ioa(ioa) { for_each_ses(ioa, ses) { if (!ses->is_resume_cand) continue; print_dev_enclosure(k, ses, buffer, "3", k); } } do { n_confirm_resume_disk_enclosure.body = buffer[toggle&1]; s_out = screen_driver(&n_confirm_resume_disk_enclosure, header_lines, NULL); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_confirm_resume_disk_enclosure.body = NULL; return rc; } /** * enclosures_fork - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int enclosures_fork(i_container *i_con) { int rc = 0, suspend_flag =0, resume_flag = 0; struct ipr_dev *ses; struct ipr_ioa *ioa; char *input; i_container *temp_i_con; for_each_icon(temp_i_con) { ses = (struct ipr_dev *)temp_i_con->data; if (ses == NULL) continue; input = temp_i_con->field_data; if (strcmp(input, "3") == 0) { if (ses->scsi_dev_data && ses->scsi_dev_data->type == IPR_TYPE_ADAPTER && ses == &ses->ioa->ioa) return RC_89_Invalid_Dev_For_Resume; if (ses->active_suspend == IOA_DEV_PORT_UNKNOWN) return RC_90_Enclosure_Is_Unknown; if (ses->active_suspend == IOA_DEV_PORT_ACTIVE) return RC_84_Enclosure_Is_Active; ses->is_resume_cand = 1; resume_flag = 1; } else if (strcmp(input, "2") == 0) { if (ses->scsi_dev_data && ses->scsi_dev_data->type == IPR_TYPE_ADAPTER && ses == &ses->ioa->ioa) return RC_88_Invalid_Dev_For_Suspend; if (ses->active_suspend == IOA_DEV_PORT_UNKNOWN) return RC_90_Enclosure_Is_Unknown; if (!strncmp((char *)&ses->serial_number, (char *)&ses->ioa->yl_serial_num[0], ESM_SERIAL_NUM_LEN)) return RC_87_No_Suspend_Same_Seri_Num; if (ses->active_suspend == IOA_DEV_PORT_SUSPEND) return RC_83_Enclosure_Is_Suspend; ses->is_suspend_cand = 1; suspend_flag = 1; } else if (strcmp(input, "1") == 0) { rc = show_enclosure_info(ses); return rc; } } if (suspend_flag) { rc =confirm_suspend_disk_enclosure(); for_each_ioa(ioa) for_each_ses(ioa, ses) ses->is_suspend_cand = 0; return rc; } if (resume_flag) { rc = confirm_resume_disk_enclosure(); for_each_ioa(ioa) for_each_ses(ioa, ses) ses->is_resume_cand = 0; return rc; } return INVALID_OPTION_STATUS; } /** * enclosures_maint - * @i_con: i_container struct * * Returns: * 0 if success / non-zero on failure FIXME **/ int enclosures_maint(i_container *i_con) { int rc, k, first_ses; struct ipr_ioa *ioa; struct ipr_dev *ses; char *buffer[2]; struct screen_output *s_out; int header_lines; int toggle=0; char product_id[IPR_PROD_ID_LEN + 1]; processing(); i_con = free_i_con(i_con); body_init_status_enclosure(buffer, n_enclosures_maint.header, &header_lines); check_current_config(false); for_each_ioa(ioa) { if (!ioa->sis64) continue; first_ses = 1; for_each_ses(ioa, ses) { ipr_strncpy_0(product_id, ses->scsi_dev_data->product_id, IPR_PROD_ID_LEN); if (!strncmp(product_id,"5888", strlen("5888")) || !strncmp(product_id, "EDR1", strlen("EDR1"))) { if (strlen(ses->physical_location) == 0) get_ses_phy_loc(ses); if (first_ses) { i_con = add_i_con(i_con, "\0",ioa); print_dev_enclosure(k, &ioa->ioa, buffer, "%1", (k | 0x1000)); first_ses = 0; } i_con = add_i_con(i_con, "\0",ses); print_dev_enclosure(k, ses, buffer, "%1", k); } } } do { n_enclosures_maint.body = buffer[toggle&1]; s_out = screen_driver(&n_enclosures_maint, header_lines, i_con); rc = s_out->rc; free(s_out); toggle++; } while (rc == TOGGLE_SCREEN); for (k = 0; k < 2; k++) { free(buffer[k]); buffer[k] = NULL; } n_enclosures_maint.body = NULL; return rc; } /** * suspend_disk_enclosure - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int suspend_disk_enclosure(char **args, int num_args) { struct ipr_dev *ses; int rc = 0; ses = find_dev(args[0]); get_ses_ioport_status(ses); if (ses->scsi_dev_data && ses->scsi_dev_data->type == IPR_TYPE_ADAPTER && ses == &ses->ioa->ioa) { fprintf(stderr,"Incorrect device type specified. Please specify a valid SAS device to suspend.\n"); return 1; } if (ses->scsi_dev_data && ses->scsi_dev_data->type != TYPE_ENCLOSURE) { fprintf(stderr, "Selected device is not SAS expander.\n"); return 1; } if (strlen(ses->physical_location) == 0) get_ses_phy_loc(ses); if (!strncmp((char *)&ses->serial_number, (char *)&ses->ioa->yl_serial_num[0], ESM_SERIAL_NUM_LEN)) { fprintf(stderr, "Could not suspend an expander from the RAID controller with the same serial number.\n"); return 1; } if (ses->active_suspend == IOA_DEV_PORT_SUSPEND) { printf("The expander is already suspended\n"); return 1; } if (ses->active_suspend == IOA_DEV_PORT_UNKNOWN) return 90; rc = ipr_suspend_device_bus(ses, &ses->res_addr[0], IPR_SDB_CHECK_AND_QUIESCE_ENC); if (!rc) { rc = ipr_write_dev_attr(ses, "state", "offline\n"); printf("Selected disk enclosure was suspended successfully.\n"); return rc; } fprintf(stderr, "Failed to suspend this SAS expander.\n"); return 1; } /** * resume_disk_enclosure - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int resume_disk_enclosure(char **args, int num_args) { struct ipr_dev *ses; int rc = 0; ses = find_dev(args[0]); get_ses_ioport_status(ses); if (ses->scsi_dev_data && ses->scsi_dev_data->type == IPR_TYPE_ADAPTER && ses == &ses->ioa->ioa) { fprintf(stderr,"Incorrect device type specified. Please specify a valid SAS device to resume.\n"); return 1; } if (ses->scsi_dev_data && ses->scsi_dev_data->type != TYPE_ENCLOSURE) { fprintf(stderr, "Selected device is not SAS expander.\n"); return 1; } if (ses->active_suspend == IOA_DEV_PORT_ACTIVE) { printf("The expander is already in active state.\n"); return 1; } if (ses->active_suspend == IOA_DEV_PORT_UNKNOWN) { printf("The expander is an unknown state.\n"); return 1; } rc = ipr_resume_device_bus(ses, &ses->res_addr[0]); if (!rc) { ipr_write_dev_attr(ses, "state", "running\n"); printf("Selected disk enclosure was resumed successfully.\n"); return rc; } printf("Failed to resume this SAS expander.\n"); return 1; } /** * query_disk_enclosure_status - * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int query_disk_enclosure_status(char **args, int num_args) { struct ipr_ioa *ioa; struct ipr_dev *ses; int rc = 0; printf("%s\n%s\n", status_hdr[14], status_sep[14]); for_each_ioa(ioa) { printf_device((struct ipr_dev *)ioa, 0x8 | 0x1000); for_each_ses(ioa, ses) { get_ses_ioport_status(ses); if (strlen(ses->physical_location) == 0) get_ses_phy_loc(ses); printf_device(ses, 0x8); } } return rc; } static const struct { char *cmd; int min_args; int unlimited_max; int max_args; int (*func)(char **, int); char *usage; } command [] = { { "show-config", 0, 0, 0, show_config, "" }, { "show-alt-config", 0, 0, 0, show_alt_config, "" }, { "show-ioas", 0, 0, 0, show_ioas, "" }, { "show-arrays", 0, 0, 0, show_arrays, "" }, { "show-battery-info", 1, 0, 1, battery_info, "sg5" }, { "show-details", 1, 0, 1, show_details, "sda" }, { "show-hot-spares", 0, 0, 0, show_hot_spares, "" }, { "show-af-disks", 0, 0, 0, show_af_disks, "" }, { "show-all-af-disks", 0, 0, 0, show_all_af_disks, "" }, { "show-jbod-disks", 0, 0, 0, show_jbod_disks, "" }, { "status", 1, 0, 1, print_status, "sda" }, { "alt-status", 1, 0, 1, print_alt_status, "sda" }, { "query-raid-create", 1, 0, 1, query_raid_create, "sg5" }, { "query-raid-delete", 1, 0, 1, query_raid_delete, "sg5" }, { "query-hot-spare-create", 1, 0, 1, query_hot_spare_create, "sg5" }, { "query-hot-spare-delete", 1, 0, 1, query_hot_spare_delete, "sg5" }, { "query-raid-consistency-check", 0, 0, 0, query_raid_consistency_check, "" }, { "query-format-for-jbod", 0, 0, 0, query_format_for_jbod, "" }, { "query-reclaim", 0, 0, 0, query_reclaim, "" }, { "query-arrays-raid-include", 0, 0, 0, query_arrays_include, "" }, { "query-devices-raid-include", 1, 0, 1, query_devices_include, "sdb" }, { "query-arrays-raid-migrate", 0, 0, 0, query_arrays_raid_migrate, "" }, { "query-devices-raid-migrate", 1, 0, 1, query_devices_raid_migrate, "sda" }, { "query-raid-levels-raid-migrate", 1, 0, 1, query_raid_levels_raid_migrate, "sda" }, { "query-stripe-sizes-raid-migrate", 2, 0, 2, query_stripe_sizes_raid_migrate, "sg5 10" }, { "query-devices-min-max-raid-migrate", 2, 0, 2, query_devices_min_max_raid_migrate, "sg5 10" }, { "query-ioas-asymmetric-access", 0, 0, 0, query_ioas_asym_access, "" }, { "query-arrays-asymmetric-access", 0, 0, 0, query_arrays_asym_access, "" }, { "query-ioa-asymmetric-access-mode", 1, 0, 1, query_ioa_asym_access_mode, "sg5" }, { "query-array-asymmetric-access-mode", 1, 0, 1, query_array_asym_access_mode, "sda" }, { "query-recovery-format", 0, 0, 0, query_recovery_format, "" }, { "query-raid-rebuild", 0, 0, 0, query_raid_rebuild, "" }, { "query-format-for-raid", 0, 0, 0, query_format_for_raid, "" }, { "query-ucode-level", 1, 0, 1, query_ucode_level, "sda" }, { "query-format-timeout", 1, 0, 1, query_format_timeout, "sg6" }, { "query-qdepth", 1, 0, 1, query_qdepth, "sda" }, { "query-tcq-enable", 1, 0, 1, query_tcq_enable, "sda" }, { "query-log-level", 1, 0, 1, query_log_level, "sg5" }, { "query-add-device", 0, 0, 0, query_add_device, "" }, { "query-remove-device", 0, 0, 0, query_remove_device, "" }, { "query-initiator-id", 2, 0, 2, query_initiator_id, "sg5 0" }, { "query-bus-speed", 2, 0, 2, query_bus_speed, "sg5 0" }, { "query-bus-width", 2, 0, 2, query_bus_width, "sg5 0" }, { "query-supported-raid-levels", 1, 0, 1, query_raid_levels, "sg5" }, { "query-include-allowed", 2, 0, 2, query_include_allowed, "sg5 5" }, { "query-max-devices-in-array", 2, 0, 2, query_max_array_devices, "sg5 5" }, { "query-min-devices-in-array", 2, 0, 2, query_min_array_devices, "sg5 5" }, { "query-min-mult-in-array", 2, 0, 2, query_min_mult_in_array, "sg5 5" }, { "query-supp-stripe-sizes", 2, 0, 2, query_supp_stripe_sizes, "sg5 5" }, { "query-recommended-stripe-size", 2, 0, 2, query_recommended_stripe_size, "sg5 5" }, { "query-path-status", 0, 0, 1, query_path_status, "sg5" }, { "query-path-details", 1, 0, 1, query_path_details, "sda" }, { "query-ioa-caching", 1, 0, 1, query_ioa_caching, "sg5" }, { "query-array-label", 1, 0, 1, query_array_label, "mylabel" }, { "set-ioa-caching", 2, 0, 2, set_ioa_caching, "sg5 [Default | Disabled]" }, { "primary", 1, 0, 1, set_primary, "sg5" }, { "secondary", 1, 0, 1, set_secondary, "sg5" }, { "set-all-primary", 0, 0, 0, set_all_primary, "" }, { "set-all-secondary", 0, 0, 0, set_all_secondary, "" }, { "query-ha-mode", 1, 0, 1, get_ha_mode, "sg5" }, { "set-ha-mode", 2, 0, 2, __set_ha_mode, "sg5 [Normal | JBOD]" }, { "set-ioa-asymmetric-access-mode", 2, 0, 2, set_ioa_asymmetric_access, "sg5 [Enabled | Disabled]" }, { "set-array-asymmetric-access-mode", 2, 0, 2, set_array_asymmetric_access, "sda [Optimized | Non-Optimized]" }, { "raid-create", 1, 1, 0, raid_create, "-r 5 -s 64 sda sdb sg6 sg7" }, { "raid-delete", 1, 0, 1, raid_delete, "sdb" }, { "raid-include", 2, 0, 17, raid_include_cmd, "sda sg6 sg7" }, { "format-for-raid", 1, 1, 0, format_for_raid, "sda sdb sdc" }, { "format-for-jbod", 1, 1, 0, format_for_jbod, "sg6 sg7 sg8" }, { "recovery-format", 1, 1, 0, recovery_format, "sda sg7" }, { "hot-spare-create", 1, 0, 1, hot_spare_create, "sg6" }, { "hot-spare-delete", 1, 0, 1, hot_spare_delete, "sg6" }, { "reclaim-cache", 1, 0, 1, reclaim, "sg5" }, { "reclaim-unknown-cache", 1, 0, 1, reclaim_unknown, "sg5" }, { "force-cache-battery-error", 1, 0, 1, force_cache_battery_error, "sg5" }, { "start-ioa-cache", 1, 0, 1, start_ioa_cache, "sg5" }, { "raid-consistency-check", 1, 0, 1, raid_consistency_check, "sg5" }, { "raid-rebuild", 1, 0, 1, raid_rebuild_cmd, "sg6" }, { "disrupt-device", 1, 0, 1, disrupt_device_cmd, "sg6" }, { "update-ucode", 2, 0, 2, update_ucode_cmd, "sg5 /root/ucode.bin" }, { "set-format-timeout", 2, 0, 2, set_format_timeout, "sg6 4" }, { "set-qdepth", 2, 0, 2, set_qdepth, "sda 16" }, { "set-tcq-enable", 2, 0, 2, set_tcq_enable, "sda 0" }, { "set-log-level", 2, 0, 2, set_log_level_cmd, "sg5 2" }, { "identify-disk", 2, 0, 2, identify_disk, "sda 1" }, { "identify-slot", 2, 0, 2, identify_slot, "U5886.001.P915059-P1-D1 1" }, { "remove-disk", 2, 0, 2, remove_disk, "sda 1" }, { "remove-slot", 2, 0, 2, remove_slot, "U5886.001.P915059-P1-D1 1" }, { "add-slot", 2, 0, 2, add_slot, "U58886.001.P915059-P1-D1 1" }, { "set-initiator-id", 3, 0, 3, set_initiator_id, "sg5 0 7" }, { "set-bus-speed", 3, 0, 3, set_bus_speed, "sg5 0 320" }, { "set-bus-width", 3, 0, 3, set_bus_width, "sg5 0 16" }, { "raid-migrate", 3, 1, 0, raid_migrate_cmd, "-r 10 [-s 256] sda sg6 sg7" }, { "get-live-dump", 1, 0, 1, get_live_dump, "sg5" }, { "query-ses-mode", 1, 0, 1, query_ses_mode, "sg8"}, { "set-ses-mode", 2, 0, 2, set_ses_mode, "sg8 2"}, { "query-disk-enclosure-status", 0, 0, 0, query_disk_enclosure_status}, { "suspend-disk-enclosure", 1, 0, 1, suspend_disk_enclosure, "sg8"}, { "resume-disk-enclosure", 1, 0, 1, resume_disk_enclosure, "sg8 "}, }; /** * non_interactive_cmd - process a command line command * @cmd: command string * @args: argument vector * @num_args: number of arguments * * Returns: * 0 if success / non-zero on failure **/ static int non_interactive_cmd(char *cmd, char **args, int num_args) { int rc, i; exit_func = cmdline_exit_func; closelog(); openlog("iprconfig", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); check_current_config(false); for (i = 0; i < ARRAY_SIZE(command); i++) { if (strcmp(cmd, command[i].cmd) != 0) continue; if (num_args < command[i].min_args) { fprintf(stderr, "Not enough arguments specified.\n"); fprintf(stderr, "Usage: iprconfig -c %s %s\n", cmd, command[i].usage); return -EINVAL; } if (!command[i].unlimited_max && num_args > command[i].max_args) { fprintf(stderr, "Too many arguments specified.\n"); fprintf(stderr, "Usage: iprconfig -c %s %s\n", cmd, command[i].usage); return -EINVAL; } rc = command[i].func(args, num_args); exit_func(); return rc; } exit_func(); usage(); return -EINVAL; } int check_sg_module() { DIR *sg_dirfd; char devpath[PATH_MAX]; sprintf(devpath, "%s", "/sys/module/sg"); sg_dirfd = opendir(devpath); if (!sg_dirfd) { exit_on_error("Failed to open sg parameter.\n"); return -1; } closedir(sg_dirfd); return 0; } /** * main - program entry point * @argc: number of arguments * @argv: argument vector * * Returns: * 0 if success / non-zero on failure **/ int main(int argc, char *argv[]) { int next_editor, next_dir, next_cmd, next_args, i, rc = 0; char parm_editor[200], parm_dir[200], cmd[200]; int non_interactive = 0; strcpy(parm_dir, DEFAULT_LOG_DIR); strcpy(parm_editor, DEFAULT_EDITOR); openlog("iprconfig", LOG_PID | /* Include the PID with each error */ LOG_CONS, /* Write to system console if there is an error sending to system logger */ LOG_USER); if (argc > 1) { next_editor = 0; next_dir = 0; next_cmd = 0; next_args = 0; for (i = 1; i < argc; i++) { if (parse_option(argv[i])) continue; else if (strcmp(argv[i], "-e") == 0) next_editor = 1; else if (strcmp(argv[i], "-k") == 0) next_dir = 1; else if (strcmp(argv[i], "-c") == 0) next_cmd = 1; else if (next_editor) { strcpy(parm_editor, argv[i]); next_editor = 0; } else if (next_dir) { strcpy(parm_dir, argv[i]); next_dir = 0; } else if (next_cmd) { strcpy(cmd, argv[i]); non_interactive = 1; next_cmd = 0; next_args = 1; } else if (next_args) { add_args = realloc(add_args, sizeof(*add_args) * (num_add_args + 1)); add_args[num_add_args] = malloc(strlen(argv[i]) + 1); strcpy(add_args[num_add_args], argv[i]); num_add_args++; } else { usage(); exit(1); } } } strcpy(log_root_dir, parm_dir); strcpy(editor, parm_editor); if (check_sg_module() == -1) system("modprobe sg"); exit_func = tool_exit_func; tool_init(0); if (non_interactive) return non_interactive_cmd(cmd, add_args, num_add_args); use_curses = 1; curses_init(); cbreak(); /* take input chars one at a time, no wait for \n */ keypad(stdscr,TRUE); noecho(); s_status.index = 0; main_menu(NULL); if (head_zdev){ check_current_config(false); ipr_cleanup_zeroed_devs(); } while (head_zdev) { struct screen_output *s_out; i_container *i_con; n_exit_confirm.body = body_init(n_exit_confirm.header, NULL); for (i = 0; i < n_exit_confirm.num_opts; i++) { n_exit_confirm.body = ipr_list_opts(n_exit_confirm.body, n_exit_confirm.options[i].key, n_exit_confirm.options[i].list_str); } n_exit_confirm.body = ipr_end_list(n_exit_confirm.body); s_out = screen_driver(&n_exit_confirm, 0, NULL); free(n_exit_confirm.body); n_exit_confirm.body = NULL; rc = s_out->rc; i_con = s_out->i_con; i_con = free_i_con(i_con); free(s_out); check_current_config(false); ipr_cleanup_zeroed_devs(); } clear(); refresh(); endwin(); exit(0); return 0; } iprutils/iprupdate.c0000644000000000000000000001126412275251125013576 0ustar rootroot/** * IBM IPR adapter microcode update utility * * (C) Copyright 2000, 2008 * International Business Machines Corporation and others. * All Rights Reserved. This program and the accompanying * materials are made available under the terms of the * Common Public License v1.0 which accompanies this distribution. * */ /* * $Header: /cvsroot/iprdd/iprutils/iprupdate.c,v 1.23 2008/11/20 01:20:20 wboyer Exp $ */ #include #include #include #include #ifndef iprlib_h #include "iprlib.h" #endif #include char *tool_name = "iprupdate"; static int force_devs; static int force_ioas; static int ioa_needs_update(struct ipr_ioa *ioa, int silent) { u32 fw_version = get_ioa_fw_version(ioa); if (fw_version >= ioa->msl) return 0; if (!silent) ioa_info(ioa, "Adapter needs microcode update\n"); return 1; } static int dev_needs_update(struct ipr_dev *dev) { struct ipr_std_inq_data std_inq_data; struct ipr_dasd_inquiry_page3 page3_inq; struct unsupported_af_dasd *unsupp_af; if (!dev->scsi_dev_data || ipr_is_volume_set(dev) || (dev->scsi_dev_data->type != TYPE_DISK && dev->scsi_dev_data->type != IPR_TYPE_AF_DISK)) return 0; memset(&std_inq_data, 0, sizeof(std_inq_data)); if (ipr_inquiry(dev, IPR_STD_INQUIRY, &std_inq_data, sizeof(std_inq_data))) return 0; if (ipr_inquiry(dev, 3, &page3_inq, sizeof(page3_inq))) return 0; unsupp_af = get_unsupp_af(&std_inq_data, &page3_inq); if (!unsupp_af) return 0; if (!disk_needs_msl(unsupp_af, &std_inq_data)) return 0; scsi_info(dev, "Device needs microcode update\n"); return 1; } static int update_ioa_fw(struct ipr_ioa *ioa, int force) { int rc; struct ipr_fw_images *list; if (!ioa_needs_update(ioa, 1) && !force) return 0; rc = get_ioa_firmware_image_list(ioa, &list); if (rc < 1) { if (ioa->should_init) ipr_log_ucode_error(ioa); return rc; } rc = ipr_update_ioa_fw(ioa, list, force); free(list); return rc; } static int update_disk_fw(struct ipr_dev *dev) { int rc; struct ipr_fw_images *list; if (polling_mode && !dev->should_init) return 0; if (!dev->scsi_dev_data || ipr_is_volume_set(dev) || (dev->scsi_dev_data->type != TYPE_DISK && dev->scsi_dev_data->type != IPR_TYPE_AF_DISK)) return 0; rc = get_dasd_firmware_image_list(dev, &list); if (rc > 0) { rc = ipr_update_disk_fw(dev, list, force_devs); free(list); } return rc; } static int download_needed(struct ipr_ioa *ioa) { struct ipr_dev *dev; int rc = 0; rc |= ioa_needs_update(ioa, 0); for_each_dev(ioa, dev) rc |= dev_needs_update(dev); return rc; } static int any_download_needed() { struct ipr_ioa *ioa; int rc = 0; tool_init(1); check_current_config(false); for_each_ioa(ioa) rc |= download_needed(ioa); return rc; } static int update_ioa(struct ipr_ioa *ioa) { struct ipr_dev *dev; int rc = 0; if (polling_mode && !ioa->should_init) return 0; rc |= update_ioa_fw(ioa, force_ioas); for_each_dev(ioa, dev) rc |= update_disk_fw(dev); return rc; } static int __update_all() { struct ipr_ioa *ioa; int rc = 0; polling_mode = 1; tool_init(1); check_current_config(false); for_each_ioa(ioa) rc |= update_ioa(ioa); return rc; } static void update_all() { __update_all(); } static void kevent_handler(char *buf) { polling_mode = 0; if (!strncmp(buf, "change@/class/scsi_host", 23) || (!strncmp(buf, "change@/devices/pci", 19) && strstr(buf, "scsi_host"))) scsi_host_kevent(buf, update_ioa); else if (!strncmp(buf, "add@/class/scsi_generic", 23) || (!strncmp(buf, "add@/devices/pci", 16) && strstr(buf, "scsi_generic"))) scsi_dev_kevent(buf, find_gen_dev, update_disk_fw); else if (!strncmp(buf, "add@/block/sd", 13)) scsi_dev_kevent(buf, find_blk_dev, update_disk_fw); } int main(int argc, char *argv[]) { int i, rc; int check_levels = 0; openlog("iprupdate", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); for (i = 1; i < argc; i++) { if (parse_option(argv[i])) continue; else if (strcmp(argv[i], "--force-devs") == 0) force_devs = 1; else if (strcmp(argv[i], "--force-ioas") == 0) force_ioas = 1; else if (strcmp(argv[i], "--check_only") == 0) check_levels = 1; else { printf("Usage: iprupdate [options]\n"); printf(" Options: --version Print iprupdate version\n"); printf(" --daemon Run as a daemon\n"); printf(" --check_only Check for needed updates\n"); exit(1); } } if (ipr_force) force_devs = force_ioas = 1; ipr_sg_required = 1; if (check_levels) return any_download_needed(); rc = __update_all(); if (!daemonize) return rc; force_devs = force_ioas = 0; ipr_daemonize(); return handle_events(update_all, 60, kevent_handler); } iprutils/iprlib.c0000644000000000000000000065552512275251164013103 0ustar rootroot/** * IBM IPR adapter utility library * * (C) Copyright 2000, 2004 * International Business Machines Corporation and others. * All Rights Reserved. This program and the accompanying * materials are made available under the terms of the * Common Public License v1.0 which accompanies this distribution. * */ /* * $Header: /cvsroot/iprdd/iprutils/iprlib.c,v 1.127 2009/10/30 18:46:39 klebers Exp $ */ #ifndef iprlib_h #include "iprlib.h" #endif static void default_exit_func() { } struct ipr_array_query_data *ipr_qac_data = NULL; int num_ioas = 0; struct ipr_ioa *ipr_ioa_head = NULL; struct ipr_ioa *ipr_ioa_tail = NULL; void (*exit_func) (void) = default_exit_func; int daemonize = 0; int ipr_debug = 0; int ipr_force = 0; int ipr_sg_required = 0; int polling_mode = 0; int ipr_fast = 0; int format_done = 0; static int ipr_mode5_write_buffer = 0; struct sysfs_dev *head_zdev = NULL; struct sysfs_dev *tail_zdev = NULL; static int ipr_force_polling = 0; static int ipr_force_uevents = 0; static char *hotplug_dir = NULL; static struct scsi_dev_data *scsi_dev_table = NULL; /* This table includes both unsupported 522 disks and disks that support being formatted to 522, but require a minimum microcode level. The disks that require a minimum level of microcode will be marked by the supported_with_min_ucode_level flag set to true. */ struct unsupported_af_dasd unsupported_af[] = { { /* 00 Mako & Hammerhead */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"DRVS "}, compare_product_id_byte: {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: false, min_ucode_level: {" "}, min_ucode_mask: {0, 0, 0, 0} }, { /* 01 Swordfish */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"DRHS "}, compare_product_id_byte: {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: false, min_ucode_level: {" "}, min_ucode_mask: {0, 0, 0, 0} }, { /* 02 Neptune */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"DNES "}, compare_product_id_byte: {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: false, min_ucode_level: {" "}, min_ucode_mask: {0, 0, 0, 0} }, { /* 03 Thornback, Stingray, & Manta */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"DMVS "}, compare_product_id_byte: {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: false, min_ucode_level: {" "}, min_ucode_mask: {0, 0, 0, 0} }, { /* 04 Discovery I */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"DDYS "}, compare_product_id_byte: {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: false, min_ucode_level: {" "}, min_ucode_mask: {0, 0, 0, 0} }, { /* 05 US73 (Discovery II) - fixme - are we supporting this drive? */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"IC35L D2 "}, compare_product_id_byte: {1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"S5DE"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 06 Apollo (Ext. Scallop 9GB) - LID: SC09 */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST318305L "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {"SC09"}, lid_mask: {1, 1, 1, 1}, supported_with_min_ucode_level: false, min_ucode_level: {"C749"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 07 Apollo (Ext. Scallop 18GB) - LID: SC18 */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST318305L "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {"SC18"}, lid_mask: {1, 1, 1, 1}, supported_with_min_ucode_level: false, min_ucode_level: {"C709"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 08 Apollo (Ext. Scallop 36GB) - LID: SC36 */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST336605L "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {"SC36"}, lid_mask: {1, 1, 1, 1}, supported_with_min_ucode_level: false, min_ucode_level: {"C709"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 09 Apollo (Ext. Scallop 73GB) - LID: SC73 */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST373405L "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {"SC73"}, lid_mask: {1, 1, 1, 1}, supported_with_min_ucode_level: false, min_ucode_level: {"C709"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 10 Apollo (non-external 9GB) - LID: AT1x */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST318305L "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {"AT1 "}, lid_mask: {1, 1, 1, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"C54E"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 11 Apollo (non-external 18GB) - LID: AT0x */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST318305L "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {"AT0 "}, lid_mask: {1, 1, 1, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"C50E"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 12 Apollo (non-external 36GB) - LID: AT0x */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST336605L "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {"AT0 "}, lid_mask: {1, 1, 1, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"C50E"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 13 Apollo (non-external 73GB) - LID: AT0x */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST373405L "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {"AT0 "}, lid_mask: {1, 1, 1, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"C50E"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 14 Odyssey (18, 36, 73GB) */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST3 53L "}, compare_product_id_byte: {1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"C51A"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 15 Odyssey (146+GB) - fixme - not in drive list; remove from table? */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST3 53L "}, compare_product_id_byte: {1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"C51A"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 16 Gemini (18, 36, 73GB) */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST3 07L "}, compare_product_id_byte: {1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"C50F"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 17 Gemini (146+GB) */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"ST3 07L "}, compare_product_id_byte: {1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"C50F"}, min_ucode_mask: {1, 1, 1, 1} }, { /* 18 Daytona */ vendor_id: {"IBM "}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"IC35L DY "}, compare_product_id_byte: {1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0}, lid: {" "}, lid_mask: {0, 0, 0, 0}, supported_with_min_ucode_level: true, min_ucode_level: {"S28C"}, min_ucode_mask: {1, 1, 1, 1} } }; struct unsupported_dasd unsupported_dasd [] = { { /* Piranha 18 GB, 15k RPM 160 MB/s - UCPR018 - 4322 */ vendor_id: {"IBMAS400"}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"UCPR018 "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, }, { /* Piranha 18 GB, 15k RPM 320 MB/s - XCPR018 - 4325 */ vendor_id: {"IBMAS400"}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"XCPR018 "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, }, { /* Piranha 35 GB, 15k RPM 160 MB/s - UCPR036 - 4323 */ vendor_id: {"IBMAS400"}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"UCPR036 "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, }, { /* Piranha 35 GB, 15k RPM 320 MB/s - XCPR036 - 4326 */ vendor_id: {"IBMAS400"}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"XCPR036 "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, }, { /* Monza 70 GB, 15k RPM 320 MB/s - XCPR073 - 4327 */ vendor_id: {"IBMAS400"}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"XCPR073 "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, }, { /* Monza 141 GB, 15k RPM 320 MB/s - XCPR146 - 4328 */ vendor_id: {"IBMAS400"}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"XCPR146 "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, }, { /* Monaco 282 GB, 15k RPM 320 MB/s - XCPR282 - 4329 */ vendor_id: {"IBMAS400"}, compare_vendor_id_byte: {1, 1, 1, 1, 1, 1, 1, 1}, product_id: {"XCPR282 "}, compare_product_id_byte: {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, } }; struct ses_table_entry { char product_id[17]; char compare_product_id_byte[17]; u32 max_bus_speed_limit; int block_15k_devices; }; static const struct ses_table_entry ses_table[] = { {"2104-DL1 ", "XXXXXXXXXXXXXXXX", 80, 0}, {"2104-TL1 ", "XXXXXXXXXXXXXXXX", 80, 0}, {"HSBP07M P U2SCSI", "XXXXXXXXXXXXXXXX", 80, 1}, {"HSBP05M P U2SCSI", "XXXXXXXXXXXXXXXX", 80, 1}, {"HSBP05M S U2SCSI", "XXXXXXXXXXXXXXXX", 80, 1}, {"HSBP06E ASU2SCSI", "XXXXXXXXXXXXXXXX", 80, 1}, {"2104-DU3 ", "XXXXXXXXXXXXXXXX", 160, 0}, {"2104-TU3 ", "XXXXXXXXXXXXXXXX", 160, 0}, {"HSBP04C RSU2SCSI", "XXXXXXX*XXXXXXXX", 160, 0}, {"HSBP06E RSU2SCSI", "XXXXXXX*XXXXXXXX", 160, 0}, {"St V1S2 ", "XXXXXXXXXXXXXXXX", 160, 0}, {"HSBPD4M PU3SCSI", "XXXXXXX*XXXXXXXX", 160, 0}, {"VSBPD1H U3SCSI", "XXXXXXX*XXXXXXXX", 160, 0} }; /*---------- static subroutine/function declearation starts here --------*/ static int sg_ioctl_by_name(char *, u8 *, void *, u32, u32, struct sense_data_t *, u32); /*---------- subroutine/function code starts here ---------*/ /** * ses_table_entry - * @ioa: ipr ioa struct * @bus: * * Returns: * ses_table_entry pointer if success / NULL on failure **/ static const struct ses_table_entry *get_ses_entry(struct ipr_ioa *ioa, int bus) { int i, j, matches; struct ipr_dev *dev = ioa->dev; const struct ses_table_entry *ste = ses_table; for (i = 0; i < ioa->num_devices; i++, dev++) { if (!dev->scsi_dev_data) continue; if (dev->scsi_dev_data->channel == bus && dev->scsi_dev_data->type == TYPE_ENCLOSURE) break; } if (i == ioa->num_devices) return NULL; if (strncmp(dev->scsi_dev_data->vendor_id, "IBM", 3)) return NULL; for (i = 0; i < ARRAY_SIZE(ses_table); i++, ste++) { for (j = 0, matches = 0; j < IPR_PROD_ID_LEN; j++) { if (ste->compare_product_id_byte[j] == 'X') { if (dev->scsi_dev_data->product_id[j] == ste->product_id[j]) matches++; else break; } else matches++; } if (matches == IPR_PROD_ID_LEN) return ste; } return NULL; } /** * get_cap_entry - Returns a pointer to a capability entry that corresponds to * the given raid level. * @supported_arrays: ipr_supported_arrays struct * @raid_level: character string representation of the raid level * * Returns: * ipr_array_cap_entry pointer if success / NULL on failure **/ struct ipr_array_cap_entry * get_cap_entry(struct ipr_supported_arrays *supported_arrays, char *raid_level) { struct ipr_array_cap_entry *cap; for_each_cap_entry(cap, supported_arrays) { if (!strcmp((char *)cap->prot_level_str, raid_level)) return cap; } return NULL; } /** * get_raid_cap_entry - Returns a pointer to a capability entry that corresponds * to the given protection level. * @supported_arrays: ipr_supported_arrays struct * @prot_level: protection level * * Returns: * ipr_array_cap_entry pointer if success / NULL on failure **/ struct ipr_array_cap_entry * get_raid_cap_entry(struct ipr_supported_arrays *supported_arrays, u8 prot_level) { struct ipr_array_cap_entry *cap; if (!supported_arrays) return NULL; for_each_cap_entry(cap, supported_arrays) { if (cap->prot_level == prot_level) return cap; } return NULL; } /** * get_prot_level_str - Returns the string representation of the protection * level the given the numeric protection level. * @supported_arrays: ipr_supported_arrays struct * @prot_level: protection level * * Returns: * protection level string if success / NULL on failure **/ char *get_prot_level_str(struct ipr_supported_arrays *supported_arrays, int prot_level) { struct ipr_array_cap_entry *cap; cap = get_raid_cap_entry(supported_arrays, prot_level); if (cap) return (char *)cap->prot_level_str; return NULL; } /** * get_vset_from_array - Given an array, return the corresponding * volume set device. * @ioa: ipr ioa struct * @array: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ struct ipr_dev *get_vset_from_array(struct ipr_ioa *ioa, struct ipr_dev *array) { struct ipr_dev *vset; for_each_vset(ioa, vset) if (vset->array_id == array->array_id) return vset; return array; } /** * get_array_from_vset - Given a volume set, return the corresponding * array device. * @ioa: ipr ioa struct * @vset: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ struct ipr_dev *get_array_from_vset(struct ipr_ioa *ioa, struct ipr_dev *vset) { struct ipr_dev *array; for_each_array(ioa, array) if (array->array_id == vset->array_id) return array; return vset; } static ssize_t sysfs_read_attr(const char *devpath, const char *attr, void *value, size_t len) { char attrpath[256]; ssize_t ret; int fd; sprintf(attrpath, "%s/%s", devpath, attr); fd = open(attrpath, O_RDONLY); if (fd < 0) return -1; ret = read(fd, value, len); close(fd); if (ret < 0) return -1; return ret; } static ssize_t sysfs_write_attr(const char *devpath, const char *attr, void *value, size_t len) { char attrpath[256]; ssize_t ret; int fd; sprintf(attrpath, "%s/%s", devpath, attr); fd = open(attrpath, O_WRONLY); if (fd < 0) return -1; ret = write(fd, value, len); close(fd); if (ret < 0) return -1; return ret; } /** * ipr_find_sysfs_dev - * @dev: ipr dev struct * @head: sysfs dev struct * * Returns: * sysfs_dev pointer if success / NULL on failure **/ struct sysfs_dev * ipr_find_sysfs_dev(struct ipr_dev *dev, struct sysfs_dev *head) { struct sysfs_dev *sdev; if (!dev->scsi_dev_data) return NULL; for (sdev = head; sdev; sdev = sdev->next) { if (!strcmp(sdev->sysfs_device_name, dev->scsi_dev_data->sysfs_device_name)) break; } return sdev; } /** * ipr_sysfs_dev_to_dev - * @sdev: sysfs dev struct * * Returns: * ipr_dev pointer if success / NULL on failure **/ struct ipr_dev *ipr_sysfs_dev_to_dev(struct sysfs_dev *sdev) { struct ipr_dev *dev; struct ipr_ioa *ioa; for_each_ioa(ioa) { for_each_dev(ioa, dev) { if (!dev->scsi_dev_data) continue; if (!strcmp(sdev->sysfs_device_name, dev->scsi_dev_data->sysfs_device_name)) return dev; } } return NULL; } /** * ipr_find_zeroed_dev - * @dev: ipr dev struct * * Returns: * results of call to ipr_find_sysfs_dev **/ struct sysfs_dev * ipr_find_zeroed_dev(struct ipr_dev *dev) { return ipr_find_sysfs_dev(dev, head_zdev); } /** * ipr_device_is_zeroed - * @dev: ipr dev struct * * Returns: * 1 if the given devices is zeroed / 0 if the device is not zeroed **/ int ipr_device_is_zeroed(struct ipr_dev *dev) { if (ipr_find_zeroed_dev(dev)) return 1; return 0; } /** * ipr_add_sysfs_dev - * @dev: ipr dev struct * @head: sysfs dev struct * @tail: sysfs dev struct * * Returns: * nothing **/ void ipr_add_sysfs_dev(struct ipr_dev *dev, struct sysfs_dev **head, struct sysfs_dev **tail) { struct sysfs_dev *sdev = ipr_find_sysfs_dev(dev, *head); if (!dev->scsi_dev_data) return; if (!sdev) { sdev = calloc(1, sizeof(struct sysfs_dev)); strcpy(sdev->sysfs_device_name, dev->scsi_dev_data->sysfs_device_name); if (!(*head)) { *tail = *head = sdev; } else { (*tail)->next = sdev; sdev->prev = *tail; *tail = sdev; } } } /** * ipr_add_zeroed_dev - * @dev: ipr dev struct * * Returns: * nothing **/ void ipr_add_zeroed_dev(struct ipr_dev *dev) { ipr_add_sysfs_dev(dev, &head_zdev, &tail_zdev); } /** * ipr_del_sysfs_dev - * @dev: ipr dev struct * @head: sysfs dev struct * @tail: sysfs dev struct * * Returns: * nothing **/ void ipr_del_sysfs_dev(struct ipr_dev *dev, struct sysfs_dev **head, struct sysfs_dev **tail) { struct sysfs_dev *sdev = ipr_find_sysfs_dev(dev, *head); if (!sdev || !dev->scsi_dev_data) return; if (sdev == *head) { *head = (*head)->next; if (!(*head)) *tail = NULL; else (*head)->prev = NULL; } else if (sdev == *tail) { *tail = (*tail)->prev; (*tail)->next = NULL; } else { sdev->next->prev = sdev->prev; sdev->prev->next = sdev->next; } } /** * ipr_del_zeroed_dev - * @dev: ipr dev struct * * Returns: * nothing **/ void ipr_del_zeroed_dev(struct ipr_dev *dev) { ipr_del_sysfs_dev(dev, &head_zdev, &tail_zdev); } /** * ipr_update_qac_with_zeroed_devs * @ioa: ipr ioa struct * * Returns: * nothing **/ void ipr_update_qac_with_zeroed_devs(struct ipr_ioa *ioa) { struct sysfs_dev *zdev; struct ipr_dev_record *dev_rcd; struct ipr_mode_pages mode_pages; struct ipr_ioa_mode_page *page; int i; if (!ioa->qac_data) return; for (i = 0; i < ioa->num_devices; i++) { zdev = ipr_find_zeroed_dev(&ioa->dev[i]); if (!zdev && ipr_is_af_dasd_device(&ioa->dev[i])) { memset(&mode_pages, 0, sizeof(mode_pages)); ipr_mode_sense(&ioa->dev[i], 0x20, &mode_pages); page = (struct ipr_ioa_mode_page *) (((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); if (page->format_completed) dev_rcd->known_zeroed = 1; } else if (zdev && ioa->dev[i].qac_entry) { dev_rcd = (struct ipr_dev_record *)ioa->dev[i].qac_entry; dev_rcd->known_zeroed = 1; } } } void set_devs_format_completed() { struct ipr_ioa *ioa; struct ipr_dev *dev; if (format_done) { for_each_ioa(ioa) { for_each_dev(ioa, dev) { if (ipr_is_af_dasd_device(dev) && ipr_device_is_zeroed(dev)) ipr_set_format_completed_bit(dev); } } format_done = 0; } } /** * ipr_cleanup_zeroed_devs - * * Returns: * nothing **/ void ipr_cleanup_zeroed_devs() { struct ipr_ioa *ioa; struct ipr_dev *dev; struct sysfs_dev *zdev; for_each_ioa(ioa) { for_each_dev(ioa, dev) { zdev = ipr_find_zeroed_dev(dev); if (!zdev) continue; if (dev->scsi_dev_data && dev->scsi_dev_data->type == TYPE_DISK) ipr_del_zeroed_dev(dev); else if (ipr_is_array_member(dev)) ipr_del_zeroed_dev(dev); else if (ipr_is_hot_spare(dev)) ipr_del_zeroed_dev(dev); } } } /** * get_max_bus_speed - * @ioa: ipr ioa struct * @bus: bus number * * Returns: * maximum bus speed value **/ int get_max_bus_speed(struct ipr_ioa *ioa, int bus) { const struct ses_table_entry *ste = get_ses_entry(ioa, bus); u32 fw_version; int max_xfer_rate = IPR_MAX_XFER_RATE; char devpath[PATH_MAX]; char value[16]; ssize_t len; sprintf(devpath, "/sys/class/scsi_host/host%s", ioa->host_name); len = sysfs_read_attr(devpath, "fw_version", value, 16); if (len < 0) return -1; sscanf(value, "%8X", &fw_version); if (ipr_force) return IPR_MAX_XFER_RATE; if (fw_version < ioa->msl) max_xfer_rate = IPR_SAFE_XFER_RATE; if (ste && ste->max_bus_speed_limit < max_xfer_rate) return ste->max_bus_speed_limit; return max_xfer_rate; } struct ioa_parms { int ccin; int scsi_id_changeable; u32 msl; char *fw_name; }; static const struct ioa_parms ioa_parms [] = { {.ccin = 0x5702, .scsi_id_changeable = 1, .msl = 0x02080029, .fw_name = "44415254" }, {.ccin = 0x1974, .scsi_id_changeable = 1, .msl = 0x02080037, .fw_name = "44415254" }, {.ccin = 0x5703, .scsi_id_changeable = 1, .msl = 0x03090059, .fw_name = "5052414D" }, {.ccin = 0x1975, .scsi_id_changeable = 1, .msl = 0x03090068, .fw_name = "5052414D" }, {.ccin = 0x5709, .scsi_id_changeable = 0, .msl = 0x030D0047, .fw_name = "5052414E" }, {.ccin = 0x1976, .scsi_id_changeable = 0, .msl = 0x030D0056, .fw_name = "5052414E" }, {.ccin = 0x570A, .scsi_id_changeable = 0, .msl = 0x020A004E, .fw_name = "44415255" }, {.ccin = 0x570B, .scsi_id_changeable = 0, .msl = 0x020A004E, .fw_name = "44415255" }, {.ccin = 0x2780, .scsi_id_changeable = 0, .msl = 0x030E0040, .fw_name = "5349530E" }, }; struct chip_details { u16 vendor; u16 device; const char *desc; }; static const struct chip_details chip_details []= { { .vendor = 0x1069, .device = 0xB166, "PCI-X" }, /* Gemstone */ { .vendor = 0x1014, .device = 0x028C, "PCI-X" }, /* Citrine */ { .vendor = 0x1014, .device = 0x0180, "PCI-X" }, /* Snipe */ { .vendor = 0x1014, .device = 0x02BD, "PCI-X" }, /* Obsidian */ { .vendor = 0x1014, .device = 0x0339, "PCI-E" }, /* Obsidian-E */ { .vendor = 0x9005, .device = 0x0503, "PCI-X" }, /* Scamp */ { .vendor = 0x1014, .device = 0x033D, "PCI-E" }, /* CRoC-FPGA */ { .vendor = 0x1014, .device = 0x034A, "PCI-E" }, /* CRoCodile */ }; /** * get_chip_details - * @ioa: ipr ioa struct * * Returns: * chip_details struct pointer if success / NULL on failure **/ static const struct chip_details *get_chip_details(struct ipr_ioa *ioa) { int i; for (i = 0; i < ARRAY_SIZE(chip_details); i++) { if (ioa->pci_vendor == chip_details[i].vendor && ioa->pci_device == chip_details[i].device) return &chip_details[i]; } return NULL; } struct ioa_details { u16 subsystem_vendor; u16 subsystem_device; const char *ioa_desc; int is_spi:1; }; static const struct ioa_details ioa_details [] = { {.subsystem_vendor = 0x1014, .subsystem_device = 0x028D, "PCI-X SAS RAID Adapter", .is_spi = 0} }; /** * get_ioa_details - * @ioa: ipr ioa struct * * Returns: * ioa_details struct pointer if success / NULL on failure **/ static const struct ioa_details *get_ioa_details(struct ipr_ioa *ioa) { int i; for (i = 0; i < ARRAY_SIZE(ioa_details); i++) { if (ioa->subsystem_vendor == ioa_details[i].subsystem_vendor && ioa->subsystem_device == ioa_details[i].subsystem_device) return &ioa_details[i]; } return NULL; } bool ipr_is_af_blk_size(struct ipr_ioa *ioa, int blk_sz) { if (blk_sz == ioa->af_block_size || (ioa->support_4k && blk_sz == IPR_AF_4K_BLOCK_SIZE)) return true; else return false; } /** * ipr_improper_device_type - * @dev: ipr dev struct * * Returns: * 1 if the device is improper / 0 if the device is not improper **/ int ipr_improper_device_type(struct ipr_dev *dev) { if (dev->rescan) return 1; if (!dev->scsi_dev_data) return 0; if (!dev->ioa->qac_data || !dev->ioa->qac_data->num_records) return 0; if (dev->scsi_dev_data->type == IPR_TYPE_AF_DISK && !dev->qac_entry && (ipr_get_blk_size(dev) == IPR_JBOD_BLOCK_SIZE || (dev->ioa->support_4k && ipr_get_blk_size(dev) == IPR_JBOD_4K_BLOCK_SIZE))) return 1; if (dev->scsi_dev_data->type == TYPE_DISK && dev->qac_entry && ipr_is_af_blk_size(dev->ioa, ipr_get_blk_size(dev))) return 1; return 0; } /** * mode_sense - * @dev: ipr dev struct * @page: page number * @buff: buffer * @sense_data sense_data_t struct * * Returns: * 0 if success / non-zero on failure **/ static int mode_sense(struct ipr_dev *dev, u8 page, void *buff, struct sense_data_t *sense_data) { u8 cdb[IPR_CCB_CDB_LEN]; int fd, rc; u8 length = IPR_MODE_SENSE_LENGTH; /* xxx FIXME? */ char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { scsi_dbg(dev, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = MODE_SENSE; cdb[2] = page; cdb[4] = length; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_FROM_DEV, sense_data, IPR_INTERNAL_DEV_TIMEOUT); close(fd); return rc; } /** * ipr_tcq_mode - * @ioa: ipr ioa struct * * Returns: * the tcq mode type **/ static enum ipr_tcq_mode ioa_get_tcq_mode(struct ipr_ioa *ioa) { struct ipr_mode_pages mode_pages; struct sense_data_t sense_data; int rc; rc = mode_sense(&ioa->ioa, 0x28, &mode_pages, &sense_data); if (rc && sense_data.sense_key == ILLEGAL_REQUEST) return IPR_TCQ_NACA; if (rc) return IPR_TCQ_DISABLE; return IPR_TCQ_FROZEN; } /** * __ioa_is_spi - Determine if the IOA is a SCSI Parallel Interface (SPI) * adapter. * @ioa: ipr ioa struct * * Returns: * 1 if the IOA is an SPI adapter / 0 otherwise **/ int __ioa_is_spi(struct ipr_ioa *ioa) { struct ipr_mode_pages mode_pages; struct sense_data_t sense_data; int rc; rc = mode_sense(&ioa->ioa, 0x28, &mode_pages, &sense_data); if (rc && sense_data.sense_key == ILLEGAL_REQUEST) return 0; return 1; } /** * ioa_is_spi - Determine if the IOA is a SCSI Parallel Interface (SPI) adapter. * @ioa: ipr ioa struct * * Returns: * 1 if the IOA is an SPI adapter / 0 otherwise **/ int ioa_is_spi(struct ipr_ioa *ioa) { const struct ioa_details *details = get_ioa_details(ioa); if (details) return details->is_spi; return __ioa_is_spi(ioa); } static const char *raid_desc = "SCSI RAID Adapter"; static const char *jbod_desc = "SCSI Adapter"; static const char *sas_raid_desc = "SAS RAID Adapter"; static const char *sas_jbod_desc = "SAS Adapter"; static const char *aux_cache_desc = "Aux Cache Adapter"; static const char *def_chip_desc = "PCI"; /** * get_bus_desc - * @ioa: ipr ioa struct * * Returns: * FIXME **/ const char *get_bus_desc(struct ipr_ioa *ioa) { const struct chip_details *chip = get_chip_details(ioa); if (!chip) return def_chip_desc; return chip->desc; } /** * get_ioa_desc - * @ioa: ipr ioa struct * * Returns: * FIXME **/ const char *get_ioa_desc(struct ipr_ioa *ioa) { const struct ioa_details *details = get_ioa_details(ioa); if (details) return details->ioa_desc; else if (ioa->is_aux_cache) return aux_cache_desc; else if (!ioa_is_spi(ioa)) { if (ioa->qac_data->num_records) return sas_raid_desc; return sas_jbod_desc; } else if (ioa->qac_data->num_records) return raid_desc; return jbod_desc; } /** * get_ioa_fw - * @ioa: ipr ioa struct * * Returns: * pointer to ioa_parms entry / NULL on failure **/ static const struct ioa_parms *get_ioa_fw(struct ipr_ioa *ioa) { int i; for (i = 0; i < ARRAY_SIZE(ioa_parms); i++) { if (ioa->ccin == ioa_parms[i].ccin) return &ioa_parms[i]; } return NULL; } /** * setup_ioa_parms * @ioa: ipr ioa struct * * Returns: * nothing **/ static void setup_ioa_parms(struct ipr_ioa *ioa) { const struct ioa_parms *ioa_parms = get_ioa_fw(ioa); ioa->scsi_id_changeable = 1; if (ioa_parms) { ioa->scsi_id_changeable = ioa_parms->scsi_id_changeable; ioa->msl = ioa_parms->msl; } } /** * find_ioa - * @host_no: host number int * * Returns: * pointer to ipr_ioa / NULL on failure **/ struct ipr_ioa *find_ioa(int host_no) { struct ipr_ioa *ioa; for_each_ioa(ioa) if (ioa->host_num == host_no) return ioa; return NULL; } /** * _find_dev - * @blk: ipr ioa struct * @compare: pointer to a comparison function * * Returns: * ipr_dev pointer if success / NULL on failure **/ static struct ipr_dev *_find_dev(char *blk, int (*compare) (struct ipr_dev *, char *)) { struct ipr_ioa *ioa; struct ipr_dev *dev; char *name = malloc(strlen(_PATH_DEV) + strlen(blk) + 1); if (!name) return NULL; if (strncmp(blk, _PATH_DEV, strlen(_PATH_DEV))) sprintf(name, _PATH_DEV"%s", blk); else sprintf(name, "%s", blk); for_each_ioa(ioa) { if (!compare(&ioa->ioa, name)) { free(name); return &ioa->ioa; } for_each_dev(ioa, dev) { if (!compare(dev, name)) { free(name); return dev; } } } free(name); return NULL; } /** * blk_compare - * @dev: ipr ioa struct * @name: character string to compare * * Returns: * integer result of the strcmp() **/ static int blk_compare(struct ipr_dev *dev, char *name) { return strcmp(dev->dev_name, name); } /** * find_blk_dev - * @blk: block device name * * Returns: * result of _find_dev() **/ struct ipr_dev *find_blk_dev(char *blk) { return _find_dev(blk, blk_compare); } /** * gen_compare - * @dev: ipr dev struct * @name: character string to compare * * Returns: * integer result of the strcmp() **/ static int gen_compare(struct ipr_dev *dev, char *name) { return strcmp(dev->gen_name, name); } /** * find_gen_dev - * @gen: generic device name * * Returns: * result of _find_dev() **/ struct ipr_dev *find_gen_dev(char *gen) { return _find_dev(gen, gen_compare); } /** * find_dev - * @name: device name to find * * Returns: * ipr_dev pointer **/ struct ipr_dev *find_dev(char *name) { struct ipr_dev *dev = find_blk_dev(name); if (!dev) dev = find_gen_dev(name); return dev; } /** * ipr_uevents_supported - indicate if uevents are supported * * Returns: * 1 if success / 0 on failure **/ static int ipr_uevents_supported() { struct ipr_ioa *ioa = ipr_ioa_head; char devpath[PATH_MAX]; char value[16]; ssize_t len; if (!ioa) return 0; sprintf(devpath, "/sys/class/scsi_host/host%s", ioa->host_name); len = sysfs_read_attr(devpath, "uevent", value, 16); return len > 0; } #define NETLINK_KOBJECT_UEVENT 15 /** * poll_forever - * @poll_func: pointer to polling function * @poll_delay: integer sleep delay value * * Returns: * nothing **/ static void poll_forever(void (*poll_func) (void), int poll_delay) { while (1) { sleep(poll_delay); poll_func(); } } /** * handle_events - * @poll_func: pointer to polling function * @poll_delay: integer sleep delay value * @prot_level: pointer to event handler function * * Returns: * 0 if poll_forever() ever returns **/ int handle_events(void (*poll_func) (void), int poll_delay, void (*kevent_handler) (char *)) { struct sockaddr_nl snl; int sock, rc, len, flags; int uevent_rcvd = ipr_force_uevents; char buf[1024]; if (ipr_force_polling) { poll_forever(poll_func, poll_delay); return 0; } memset(&snl, 0, sizeof(snl)); snl.nl_family = AF_NETLINK; snl.nl_pid = getpid(); snl.nl_groups = 1; sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); if (sock == -1) { syslog_dbg("Failed to get socket\n"); poll_forever(poll_func, poll_delay); return 0; } rc = bind(sock, (struct sockaddr *)&snl, sizeof(snl)); if (rc < 0) { syslog_dbg("Failed to bind to socket\n"); close(sock); poll_forever(poll_func, poll_delay); return 0; } if (!ipr_force_uevents && !ipr_uevents_supported()) { if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { syslog(LOG_ERR, "Failed to fcntl socket\n"); close(sock); poll_forever(poll_func, poll_delay); return 0; } } while (1) { len = recv(sock, &buf, sizeof(buf), 0); if (!uevent_rcvd && len < 0) { poll_func(); sleep(poll_delay); continue; } if (len < 0) { syslog_dbg("kevent recv failed.\n"); poll_func(); sleep(poll_delay); continue; } if (!uevent_rcvd) { flags = fcntl(sock, F_GETFL); if (flags == -1) { syslog_dbg("F_GETFL Failed\n"); poll_func(); sleep(poll_delay); continue; } if (flags & O_NONBLOCK) { if (fcntl(sock, F_SETFL, (flags & ~O_NONBLOCK)) == -1) { syslog_dbg("F_SETFL Failed\n"); poll_func(); sleep(poll_delay); continue; } } uevent_rcvd = 1; } kevent_handler(buf); } close(sock); return 0; } /** * parse_option - parse options from the command line * @opt: character string option value * * Returns: * 1 if success / 0 if the option isn't recognized **/ int parse_option(char *opt) { if (strcmp(opt, "--version") == 0) { printf("%s: %s\n", tool_name, IPR_VERSION_STR); exit(0); } if (strcmp(opt, "--daemon") == 0) daemonize = 1; else if (strcmp(opt, "--debug") == 0) ipr_debug = 1; else if (strcmp(opt, "--force") == 0) ipr_force = 1; else if (strcmp(opt, "--use-polling") == 0) ipr_force_polling = 1; else if (strcmp(opt, "--use-uevents") == 0) ipr_force_uevents = 1; else if (strcmp(opt, "--fast") == 0) ipr_fast = 1; else if (strcmp(opt, "--deferred-write-buffer") == 0) ipr_mode5_write_buffer = 0; else if (strcmp(opt, "--mode5-write-buffer") == 0) ipr_mode5_write_buffer = 1; else return 0; return 1; } /** * get_pci_attr - * @sysfs_pci_device: sysfs_device struct * @attr: attribute string * * Returns: * pci attribute value if success / 0 on failure **/ static int get_pci_attr(char *devpath, char *attr) { int temp, fd, len; char path[PATH_MAX]; char data[200]; sprintf(path, "%s/%s", devpath, attr); if ((fd = open(path, O_RDONLY)) < 0) { syslog_dbg("Failed to open %s\n", path); return 0; } len = read(fd, data, 200); if (len < 0) { syslog_dbg("Failed to read %s\n", path); close(fd); return 0; } sscanf(data, "0x%4X", &temp); close(fd); return temp; } /** * get_pci_attrs - sets vendor and device information * @ioa: ipr ioa struct * @sysfs_pci_device: sysfs_device struct * * Returns: * nothing **/ static void get_pci_attrs(struct ipr_ioa *ioa, char *pci_path) { ioa->subsystem_vendor = get_pci_attr(pci_path, "subsystem_vendor"); ioa->subsystem_device = get_pci_attr(pci_path, "subsystem_device"); ioa->pci_vendor = get_pci_attr(pci_path, "vendor"); ioa->pci_device = get_pci_attr(pci_path, "device"); } static struct ipr_ioa *old_ioa_head; static struct ipr_ioa *old_ioa_tail; static int old_num_ioas; static struct scsi_dev_data *old_scsi_dev_table; struct ipr_array_query_data *old_qac_data; /** * free_current_config - frees current configuration data structures * * Returns: * nothing **/ static void free_current_config() { struct ipr_ioa *ioa; for (ioa = ipr_ioa_head; ioa;) { ioa = ioa->next; free(ipr_ioa_head); ipr_ioa_head = ioa; } free(ipr_qac_data); free(scsi_dev_table); ipr_ioa_head = NULL; ipr_ioa_tail = NULL; num_ioas = 0; scsi_dev_table = NULL; ipr_qac_data = NULL; } /** * save_old_config - saves current configuration data structures * * Returns: * nothing **/ static void save_old_config() { if (old_ioa_head) { free_current_config(); return; } old_ioa_head = ipr_ioa_head; old_ioa_tail = ipr_ioa_tail; old_num_ioas = num_ioas; old_scsi_dev_table = scsi_dev_table; old_qac_data = ipr_qac_data; ipr_qac_data = NULL; scsi_dev_table = NULL; ipr_ioa_head = NULL; ipr_ioa_tail = NULL; num_ioas = 0; } /** * free_old_config - frees old configuration data structures * * Returns: * nothing **/ static void free_old_config() { struct ipr_ioa *ioa; /* Free up all the old memory */ for (ioa = old_ioa_head; ioa;) { ioa = ioa->next; free(old_ioa_head); old_ioa_head = ioa; } free(old_qac_data); free(old_scsi_dev_table); old_ioa_head = NULL; old_ioa_tail = NULL; old_num_ioas = 0; old_scsi_dev_table = NULL; old_qac_data = NULL; } /** * same_ioa - compares two ioas to determine if they are the same * @first: ipr ioa struct * @second: ipr ioa struct * * Returns: * 0 if ioas are not the same / 1 if ioas are the same **/ static int same_ioa(struct ipr_ioa *first, struct ipr_ioa *second) { if (strcmp(first->pci_address, second->pci_address)) return 0; if (strcmp(first->host_name, second->host_name)) return 0; if (first->ccin != second->ccin) return 0; if (first->host_num != second->host_num) return 0; if (first->pci_vendor != second->pci_vendor) return 0; if (first->pci_device != second->pci_device) return 0; if (first->subsystem_vendor != second->subsystem_vendor) return 0; if (first->subsystem_device != second->subsystem_device) return 0; return 1; } /** * same_scsi_dev - compares two scsi devices to determin if they are the same * @first: scsi_dev_data struct * @second: scsi_dev_data struct * * Returns: * 0 if scsi devs are not the same / 1 if scsi devs are the same **/ static int same_scsi_dev(struct scsi_dev_data *first, struct scsi_dev_data *second) { if (!first || !second) return 0; if (first->host != second->host) return 0; if (first->channel != second->channel) return 0; if (first->id != second->id) return 0; if (first->lun != second->lun) return 0; if (first->type != second->type) return 0; if (first->online != second->online) return 0; if (strcmp(first->vendor_id, second->vendor_id)) return 0; if (strcmp(first->product_id, second->product_id)) return 0; if (strcmp(first->sysfs_device_name, second->sysfs_device_name)) return 0; if (strcmp(first->dev_name, second->dev_name)) return 0; if (strcmp(first->gen_name, second->gen_name)) return 0; return 1; } /** * same_dev_rcd - compares two device records to determine if they are the same * @first: ipr_dev struct * @second: ipr_dev struct * * Returns: * 0 if devices are not the same / 1 if devices are the same **/ static int same_dev_rcd(struct ipr_dev *first, struct ipr_dev *second) { if (memcmp(&first->res_addr, &second->res_addr, sizeof(first->res_addr))) return 0; if (memcmp(first->vendor_id, second->vendor_id, IPR_VENDOR_ID_LEN)) return 0; if (memcmp(first->product_id, second->product_id, IPR_PROD_ID_LEN)) return 0; if (memcmp(first->serial_number, second->serial_number, IPR_SERIAL_NUM_LEN)) return 0; return 1; } /** * same_dev - compares two devices to determine if they are the same * @first: ipr_dev struct * @second: ipr_dev struct * * Returns: * 0 if devices are not the same / 1 if devices are the same **/ static int same_dev(struct ipr_dev *first, struct ipr_dev *second) { if (strcmp(first->dev_name, second->dev_name)) return 0; if (strcmp(first->gen_name, second->gen_name)) return 0; if (!first->scsi_dev_data && !second->scsi_dev_data) { if (!ipr_is_af_dasd_device(first) || !ipr_is_af_dasd_device(second)) return 0; if (!same_dev_rcd(first, second)) return 0; } else if (!same_scsi_dev(first->scsi_dev_data, second->scsi_dev_data)) return 0; return 1; } /** * dev_init_allowed - * @dev: ipr dev struct * * Returns: * 1 if initialization is allowed / 0 if initialization is not allowed **/ static int dev_init_allowed(struct ipr_dev *dev) { struct ipr_query_res_state res_state; if (!ipr_is_af_dasd_device(dev)) return 1; if (!ipr_query_resource_state(dev, &res_state)) { if (!res_state.read_write_prot && !res_state.prot_dev_failed) return 1; } return 0; } /** * resolve_dev - * @new: ipr dev struct * @old: ipr dev struct * * Returns: * nothing **/ static void resolve_dev(struct ipr_dev *new, struct ipr_dev *old) { new->init_not_allowed = !dev_init_allowed(new); if (!old->init_not_allowed || new->init_not_allowed) new->should_init = 0; if (!new->ioa->sis64 && new->ioa->is_secondary && !old->ioa->is_secondary && ipr_is_af_dasd_device(new) && new->scsi_dev_data) new->rescan = 1; if (!new->ioa->sis64 && !new->ioa->is_secondary && old->ioa->is_secondary && ipr_is_af_dasd_device(new) && !new->scsi_dev_data) new->rescan = 1; } /** * resolve_ioa - * @ioa: ipr ioa struct * @old_ioa: ipr ioa struct * * Returns: * nothing **/ static void resolve_ioa(struct ipr_ioa *ioa, struct ipr_ioa *old_ioa) { struct ipr_dev *dev, *old_dev; ioa->should_init = 0; for_each_dev(ioa, dev) { for_each_dev(old_ioa, old_dev) { if (!same_dev(dev, old_dev)) continue; memcpy(&dev->attr, &old_dev->attr, sizeof(dev->attr)); dev->rescan = old_dev->rescan; resolve_dev(dev, old_dev); break; } } } /** * resolve_old_config - * * Returns: * nothing **/ static void resolve_old_config() { struct ipr_ioa *ioa, *old_ioa; struct ipr_dev *dev; for_each_ioa(ioa) { ioa->should_init = 1; for_each_dev(ioa, dev) dev->should_init = 1; } for_each_ioa(ioa) { __for_each_ioa(old_ioa, old_ioa_head) { if (!same_ioa(ioa, old_ioa)) continue; resolve_ioa(ioa, old_ioa); break; } } free_old_config(); } struct ipr_pci_slot { char slot_name[PATH_MAX]; char physical_name[PATH_MAX]; char pci_device[PATH_MAX]; }; static struct ipr_pci_slot *pci_slot; static unsigned int num_pci_slots; /** * ipr_select_phy_location - * @dirent: dirent struct * * Returns: * 1 if d_name == "phy_location"/ 0 otherwise **/ static int ipr_select_phy_location(const struct dirent *dirent) { if (strstr(dirent->d_name, "phy_location")) return 1; return 0; } static int ipr_select_pci_address(const struct dirent *dirent) { if (strstr(dirent->d_name, "address")) return 1; return 0; } /** * read_attr_file - * @path: path name of device/file to open * @out: character array * * Returns: * 0 if success / non-zero on failure **/ static int read_attr_file(char *path, char *out, int size) { int fd, len; if ((fd = open(path, O_RDONLY)) < 0) { syslog_dbg("Failed to open %s\n", path); return -EIO; } len = read(fd, out, size); if (len < 0) { syslog_dbg("Failed to read %s\n", path); close(fd); return -EIO; } if (out[strlen(out) - 1] == '\n') out[strlen(out) - 1] = '\0'; close(fd); return 0; } /** * ipr_add_slot_location - * @path: path of device/file to open * @name: name of device/file to open * * Returns: * 0 if success / non-zero on failure **/ static int ipr_add_slot_location(char *path, char *name) { struct ipr_pci_slot *slot; char fpath[PATH_MAX]; int rc; sprintf(fpath, "%s%s/phy_location", path, name); pci_slot = realloc(pci_slot, sizeof(*pci_slot) * (num_pci_slots + 1)); slot = &pci_slot[num_pci_slots]; memset(slot, 0, sizeof(*slot)); strcpy(slot->slot_name, name); rc = read_attr_file(fpath, slot->physical_name, sizeof(slot->physical_name)); if (rc) { pci_slot = realloc(pci_slot, sizeof(*pci_slot) * num_pci_slots); return rc; } num_pci_slots++; return 0; } /** * ipr_add_slot_address - * @path: path of device/file to open * @name: name of device/file to open * * Returns: * 0 if success / non-zero on failure **/ static int ipr_add_slot_address(char *path, char *name) { struct ipr_pci_slot *slot; char fpath[PATH_MAX]; int rc; sprintf(fpath, "%s%s/address", path, name); pci_slot = realloc(pci_slot, sizeof(*pci_slot) * (num_pci_slots + 1)); slot = &pci_slot[num_pci_slots]; memset(slot, 0, sizeof(*slot)); strcpy(slot->slot_name, name); strcpy(slot->physical_name, name); rc = read_attr_file(fpath, slot->pci_device, sizeof(slot->pci_device)); if (rc) { pci_slot = realloc(pci_slot, sizeof(*pci_slot) * num_pci_slots); return rc; } strcat(slot->pci_device, ".0"); num_pci_slots++; return 0; } /** * ipr_get_pci_slots - * * Returns: * nothing **/ static void ipr_get_pci_slots() { char rootslot[PATH_MAX], slot[PATH_MAX]; char slotpath[PATH_MAX], attr[PATH_MAX]; char devspec[PATH_MAX], locpath[PATH_MAX]; char loc_code[1024], *last_hyphen, *prev_hyphen; int num_slots, i, j, rc, num_attrs; int slot_found = 0; struct dirent **slotdir, **dirent; struct stat statbuf; struct ipr_ioa *ioa; sprintf(rootslot, "/sys/bus/pci/slots/"); num_slots = scandir(rootslot, &slotdir, NULL, alphasort); if (num_slots <= 0) { return; } for (i = 0; i < num_slots; i++) { sprintf(slot, "%s%s", rootslot, slotdir[i]->d_name); rc = scandir(slot, &dirent, ipr_select_phy_location, alphasort); if (rc > 0) { ipr_add_slot_location(rootslot, slotdir[i]->d_name); while (rc--) free(dirent[rc]); free(dirent); } else { rc = scandir(slot, &dirent, ipr_select_pci_address, alphasort); if (rc <= 0) continue; ipr_add_slot_address(rootslot, slotdir[i]->d_name); while (rc--) free(dirent[rc]); free(dirent); } } while (num_slots--) free(slotdir[num_slots]); free(slotdir); for (i = 0; i < num_pci_slots; i++) { if (!pci_slot[i].pci_device) continue; sprintf(slotpath, "/sys/bus/pci/devices/%s/", pci_slot[i].slot_name); num_attrs = scandir(slotpath, &slotdir, NULL, alphasort); if (num_attrs <= 0) continue; for (j = 0; j < num_attrs; j++) { sprintf(attr, "%s/%s", slotpath, slotdir[j]->d_name); rc = stat(attr, &statbuf); if (rc || !S_ISDIR(statbuf.st_mode)) continue; if (!strcmp(slotdir[j]->d_name, ".")) continue; if (!strcmp(slotdir[j]->d_name, "..")) continue; strcpy(pci_slot[i].pci_device, slotdir[j]->d_name); break; } while (num_attrs--) free(slotdir[num_attrs]); free(slotdir); } for_each_ioa(ioa) ioa->physical_location[0] = '\0'; for_each_ioa(ioa) { slot_found = 0; for (i = 0; i < num_pci_slots; i++) { if (strcmp(pci_slot[i].pci_device, ioa->pci_address) && strcmp(pci_slot[i].slot_name, ioa->pci_address)) continue; strcpy(ioa->physical_location, pci_slot[i].physical_name); slot_found = 1; break; } if (!slot_found) { sprintf(attr, "/sys/bus/pci/devices/%s/devspec", ioa->pci_address); rc = read_attr_file(attr, devspec, PATH_MAX); if (rc) continue; sprintf(locpath, "/proc/device-tree%s/ibm,loc-code", devspec); rc = read_attr_file(locpath, loc_code, sizeof(loc_code)); if (rc) continue; last_hyphen = strrchr(loc_code, '-'); if (last_hyphen && last_hyphen[1] == 'T') { *last_hyphen = '\0'; prev_hyphen = strrchr(loc_code, '-'); if (prev_hyphen && prev_hyphen[1] != 'C') *last_hyphen = '-'; } strcpy(ioa->physical_location, loc_code); } } free(pci_slot); pci_slot = NULL; num_pci_slots = 0; } /** * tool_init - * @save_state: integer flag - whether or not to save the old config * * Returns: * nothing **/ void tool_init(int save_state) { int temp, fw_type; struct ipr_ioa *ipr_ioa; DIR *dirfd, *host_dirfd; struct dirent *dent, *host_dent; ssize_t len; save_old_config(); dirfd = opendir("/sys/bus/pci/drivers/ipr"); if (!dirfd) { syslog_dbg("Failed to open ipr driver bus. %m\n"); dirfd = opendir("/sys/bus/pci/drivers/ipr"); if (!dirfd) { syslog_dbg("Failed to open ipr driver bus on second attempt. %m\n"); return; } } while ((dent = readdir(dirfd)) != NULL) { int channel, bus, device, function; char devpath[PATH_MAX]; char buff[256]; if (sscanf(dent->d_name, "%x:%x:%x.%x", &channel, &bus, &device, &function) != 4) continue; ipr_ioa = (struct ipr_ioa*)malloc(sizeof(struct ipr_ioa)); memset(ipr_ioa,0,sizeof(struct ipr_ioa)); /* PCI address */ strcpy(ipr_ioa->pci_address, dent->d_name); ipr_ioa->host_num = -1; sprintf(devpath, "/sys/bus/pci/drivers/ipr/%s", dent->d_name); host_dirfd = opendir(devpath); if (!host_dirfd) { exit_on_error("Failed to open scsi_host class.\n"); continue; } while ((host_dent = readdir(host_dirfd)) != NULL) { char scsipath[PATH_MAX]; char fw_str[256]; if (strncmp(host_dent->d_name, "host", 4)) continue; if (sscanf(host_dent->d_name, "host%d", &ipr_ioa->host_num) != 1) continue; strcpy(ipr_ioa->host_name, host_dent->d_name); get_pci_attrs(ipr_ioa, devpath); sprintf(scsipath, "%s/%s/scsi_host/%s", devpath, ipr_ioa->host_name, ipr_ioa->host_name); len = sysfs_read_attr(scsipath, "fw_type", fw_str, 256); if (len < 0) fw_type = 0; else sscanf(fw_str, "%d", &fw_type); if (ipr_debug) syslog(LOG_INFO, "tool_init: fw_type attr = %d.\n", fw_type); break; } closedir(host_dirfd); if (ipr_ioa->host_num < 0) { exit_on_error("No SCSI Host found on IPR device %s\n", ipr_ioa->pci_address); free(ipr_ioa); continue; } if (fw_type == IPR_SIS64) { sprintf(devpath, "/sys/bus/scsi/devices/%d:%d:0:0", ipr_ioa->host_num, IPR_IOAFP_VIRTUAL_BUS); ipr_ioa->sis64 = 1; } else sprintf(devpath, "/sys/bus/scsi/devices/%d:255:255:255", ipr_ioa->host_num); len = sysfs_read_attr(devpath, "model", buff, 16); if (len < 0 || (sscanf(buff, "%4X", &temp) != 1)) { exit_on_error("Cannot read SCSI device model.\n"); continue; } ipr_ioa->ccin = temp; setup_ioa_parms(ipr_ioa); ipr_ioa->next = NULL; ipr_ioa->num_raid_cmds = 0; if (ipr_ioa_tail) { ipr_ioa_tail->next = ipr_ioa; ipr_ioa_tail = ipr_ioa; } else ipr_ioa_tail = ipr_ioa_head = ipr_ioa; num_ioas++; } closedir(dirfd); if (!save_state) free_old_config(); ipr_get_pci_slots(); return; } /** * ipr_reset_adapter - * @ioa: ipr ioa struct * * Returns: * nothing **/ void ipr_reset_adapter(struct ipr_ioa *ioa) { char devpath[PATH_MAX]; sprintf(devpath, "/sys/class/scsi_host/%s", ioa->host_name); sysfs_write_attr(devpath, "reset_host", "1", 1); } /** * ipr_read_host_attr - * @ioa: ipr ioa struct * @name: attribute name * @value: value to be set * @value_len: length of value string * * Returns: * 0 if success / non-zero on failure **/ int ipr_read_host_attr(struct ipr_ioa *ioa, char *name, void *value, size_t value_len) { ssize_t len; char path[PATH_MAX]; sprintf(path, "/sys/class/scsi_host/%s", ioa->host_name); len = sysfs_read_attr(path, name, value, value_len); if (len < 0) { ioa_dbg(ioa, "Failed to read %s attribute. %m\n", name); return -EIO; } return 0; } /** * ipr_write_host_attr - * @ioa: ipr ioa struct * @name: attribute to be written * @value: new value * @value_len: length of value string * * Returns: * 0 if success / non-zero on failure **/ int ipr_write_host_attr(struct ipr_ioa *ioa, char *name, void *value, size_t value_len) { ssize_t len; char path[PATH_MAX]; sprintf(path, "/sys/class/scsi_host/%s", ioa->host_name); len = sysfs_write_attr(path, name, value, value_len); if (len < 0) { ioa_dbg(ioa, "Failed to write %s attribute. %m\n", name); return -EIO; } return 0; } /** * ipr_cmds_per_lun - * @ioa: ipr ioa struct * * Returns: * number of commands per lun if success / 6 on failure **/ int ipr_cmds_per_lun(struct ipr_ioa *ioa) { char value[100]; int rc; rc = ipr_read_host_attr(ioa, "cmd_per_lun", value, 100); if (rc) return 6; return strtoul(value, NULL, 10); } /** * ipr_scan - * @ioa: ipr ioa struct * @bus: bus number * @id: id number * @lun: lun number (RAID) level for new array * * Returns: * nothing **/ void ipr_scan(struct ipr_ioa *ioa, int bus, int id, int lun) { char buf[100]; char devpath[PATH_MAX]; if (lun < 0) { if (id < 0) sprintf(buf, "%d - -", bus); else sprintf(buf, "%d %d -", bus, id); } else { if (id < 0) sprintf(buf, "%d - %d", bus, lun); else sprintf(buf, "%d %d %d", bus, id, lun); } sprintf(devpath, "/sys/class/scsi_host/%s", ioa->host_name); sysfs_write_attr(devpath, "scan", buf, 100); } /** * __ipr_query_array_config - perform a query array configuration ioctl * @ioa: ipr ioa struct * @fd: file descriptor * @allow_rebuld_refresh: allow rebuild refresh flag * @set_array_id: set array id flag * @array_id: array id number * @buff: data buffer * * Returns: * 0 if success / non-zero on failure **/ int __ipr_query_array_config(struct ipr_ioa *ioa, int fd, bool allow_rebuild_refresh, bool set_array_id, bool vset_migrate_query, int array_id, struct ipr_array_query_data *buff) { int length = IPR_QAC_BUFFER_SIZE; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; if (!ioa->sis64) length = IPR_SIS32_QAC_BUFFER_SIZE; ioa->nr_ioa_microcode = 0; memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_QUERY_ARRAY_CONFIG; if (vset_migrate_query) { cdb[1] = 0x08; cdb[2] = array_id; if (ioa->sis64) cdb[3] = 0x80; } else if (set_array_id) { cdb[1] = 0x01; cdb[2] = array_id; if (ioa->sis64) cdb[3] = 0x80; } else if (allow_rebuild_refresh) cdb[1] = 0; else cdb[1] = 0x80; /* Prohibit Rebuild Candidate Refresh */ cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; if (ioa->sis64) { cdb[10] = (length & 0xff000000) >> 24; cdb[11] = (length & 0xff0000) >> 16; } rc = sg_ioctl(fd, cdb, buff->u.buf, length, SG_DXFER_FROM_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0) { if (sense_data.sense_key != ILLEGAL_REQUEST || ipr_debug) ioa_cmd_err(ioa, &sense_data, "Query Array Config", rc); else if (sense_data.sense_key == NOT_READY && sense_data.add_sense_code == 0x40 && sense_data.add_sense_code_qual == 0x85) ioa->nr_ioa_microcode = 1; } if (ioa->sis64) { buff->resp_len = ntohl(buff->u.buf64.resp_len); buff->num_records = ntohl(buff->u.buf64.num_records); buff->data = buff->u.buf64.data; buff->hdr_len = 8; } else { buff->resp_len = ntohs(buff->u.buf32.resp_len); buff->num_records = ntohs(buff->u.buf32.num_records); buff->data = buff->u.buf32.data; buff->hdr_len = 4; } return rc; } /** * ipr_query_array_config - entry point to query array configuration ioctl call * @ioa: ipr ioa struct * @allow_rebuld_refresh: allow rebuild refresh flag * @set_array_id: set array id flag * @array_id: array id number * @buff: data buffer * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_array_config(struct ipr_ioa *ioa, bool allow_rebuild_refresh, bool set_array_id, bool vset_migrate_query, int array_id, struct ipr_array_query_data *buff) { int fd, rc; ioa->nr_ioa_microcode = 0; if (strlen(ioa->ioa.gen_name) == 0) { scsi_err((&ioa->ioa), "Adapter sg device does not exist\n"); return -ENOENT; } fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", ioa->ioa.gen_name); return errno; } rc = flock(fd, LOCK_EX); if (rc) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not lock %s. %m\n", ioa->ioa.gen_name); close(fd); return errno; } rc = __ipr_query_array_config(ioa, fd, allow_rebuild_refresh, set_array_id, vset_migrate_query, array_id, buff); close(fd); return rc; } /** * ipr_query_multi_ioa_status - * @ioa: ipr ioa struct * @buff: data buffer * @len: length * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_multi_ioa_status(struct ipr_ioa *ioa, void *buff, u32 len) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; if (strlen(ioa->ioa.gen_name) == 0) return -ENOENT; fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", ioa->ioa.gen_name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_MAINTENANCE_IN; cdb[1] = IPR_QUERY_MULTI_ADAPTER_STATUS; cdb[6] = len >> 24; cdb[7] = (len >> 16) & 0xff; cdb[8] = (len >> 8) & 0xff; cdb[9] = len & 0xff; rc = sg_ioctl(fd, cdb, buff, len, SG_DXFER_FROM_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0) { if (sense_data.sense_key != ILLEGAL_REQUEST) ioa_cmd_err(ioa, &sense_data, "Query Multi Adapter Status", rc); else if (sense_data.sense_key == NOT_READY && sense_data.add_sense_code == 0x40 && sense_data.add_sense_code_qual == 0x85) ioa->nr_ioa_microcode = 1; } close(fd); return rc; } /** * ipr_start_array - Start array protection for an array * @ioa: ipr ioa struct * @cmd: char pointer * @stripe_size: stripe size for new array * @prot_level: protection (RAID) level for new array * @hot_spare: flag to indicate hot spare usage * * Returns: * 0 if success / non-zero on failure **/ static int ipr_start_array(struct ipr_ioa *ioa, char *cmd, int stripe_size, int prot_level, int hot_spare) { int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; int length = ioa->qac_data->resp_len; if (strlen(ioa->ioa.gen_name) == 0) return -ENOENT; fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", ioa->ioa.gen_name); return errno; } ipr_update_qac_with_zeroed_devs(ioa); memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_START_ARRAY_PROTECTION; if (hot_spare) cdb[1] = 0x01; cdb[4] = (u8)(stripe_size >> 8); cdb[5] = (u8)(stripe_size & 0xff); cdb[6] = prot_level; cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; if (ioa->sis64) { cdb[10] = (length & 0xff000000) >> 24; cdb[11] = (length & 0xff0000) >> 16; } rc = sg_ioctl(fd, cdb, ioa->qac_data->u.buf, length, SG_DXFER_TO_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0) ioa_cmd_err(ioa, &sense_data, "Start Array Protection", rc); close(fd); return rc; } /** * ipr_start_array_protection - Start array protection for an array * @ioa: ipr ioa struct * @stripe_size: stripe size for new array * @prot_level: protection (RAID) level for new array * * Returns: * 0 if success / non-zero on failure **/ int ipr_start_array_protection(struct ipr_ioa *ioa, int stripe_size, int prot_level) { return ipr_start_array(ioa, "Start Array Protection", stripe_size, prot_level, 0); } /** * ipr_migrate_array_protection - Migrate array protection for an array * @ioa: ipr ioa struct * @qac_data: struct ipr_array_query_data * @fd: file descriptor * @stripe_size: new or existing stripe size for array * @prot_level: new protection (RAID) level for array * * Returns: * 0 if success / non-zero on failure **/ int ipr_migrate_array_protection(struct ipr_ioa *ioa, struct ipr_array_query_data *qac_data, int fd, int stripe_size, int prot_level) { u8 cdb[IPR_CCB_CDB_LEN]; char *cmd = "migrate array protection"; struct sense_data_t sense_data; int rc; int length = qac_data->resp_len; memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_MIGRATE_ARRAY_PROTECTION; cdb[4] = (u8)(stripe_size >> 8); cdb[5] = (u8)(stripe_size & 0xff); cdb[6] = prot_level; cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; if (ioa->sis64) { cdb[10] = (length & 0xff000000) >> 24; cdb[11] = (length & 0xff0000) >> 16; } rc = sg_ioctl(fd, cdb, qac_data->u.buf, length, SG_DXFER_TO_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0) ioa_cmd_err(ioa, &sense_data, cmd, rc); return rc; } /** * ipr_add_hot_spare - Adds a hot spare to an array * @ioa: ipr ioa struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_add_hot_spare(struct ipr_ioa *ioa) { return ipr_start_array(ioa, "Create hot spare", 0, 0, 1); } /** * ipr_stop_array - Stop array protection for an array * @ioa: ipr ioa struct * @cmd: command string * @hot_spare: flag to indicate hot spare usage * * Returns: * 0 if success / non-zero on failure **/ static int ipr_stop_array(struct ipr_ioa *ioa, char *cmd, int hot_spare) { int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; int length = ioa->qac_data->resp_len; if (strlen(ioa->ioa.gen_name) == 0) return -ENOENT; fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", ioa->ioa.gen_name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_STOP_ARRAY_PROTECTION; if (hot_spare) cdb[1] = 0x01; cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; if (ioa->sis64) { cdb[10] = (length & 0xff000000) >> 24; cdb[11] = (length & 0xff0000) >> 16; } rc = sg_ioctl(fd, cdb, ioa->qac_data->u.buf, length, SG_DXFER_TO_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0) ioa_cmd_err(ioa, &sense_data, cmd, rc); close(fd); return rc; } /** * ipr_stop_array_protection - Stop array protection for an array * @ioa: ipr ioa struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_stop_array_protection(struct ipr_ioa *ioa) { return ipr_stop_array(ioa, "Stop Array Protection", 0); } /** * ipr_remove_hot_spare - remove a hot spare from an array * @ioa: ipr ioa struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_remove_hot_spare(struct ipr_ioa *ioa) { return ipr_stop_array(ioa, "Delete hot spare", 1); } /** * ipr_rebuild_device_data - * @ioa: ipr ioa struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_rebuild_device_data(struct ipr_ioa *ioa) { int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; int length = ioa->qac_data->resp_len; if (strlen(ioa->ioa.gen_name) == 0) return -ENOENT; fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", ioa->ioa.gen_name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_REBUILD_DEVICE_DATA; cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; if (ioa->sis64) { cdb[10] = (length & 0xff000000) >> 24; cdb[11] = (length & 0xff0000) >> 16; } rc = sg_ioctl(fd, cdb, ioa->qac_data->u.buf, length, SG_DXFER_TO_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0) ioa_cmd_err(ioa, &sense_data, "Rebuild Device Data", rc); close(fd); return rc; } /** * ipr_resync_array - * @ioa: ipr ioa struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_resync_array(struct ipr_ioa *ioa) { int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; int length = ioa->qac_data->resp_len; if (strlen(ioa->ioa.gen_name) == 0) return -ENOENT; fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", ioa->ioa.gen_name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_RESYNC_ARRAY_PROTECTION; cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; if (ioa->sis64) { cdb[10] = (length & 0xff000000) >> 24; cdb[11] = (length & 0xff0000) >> 16; } rc = sg_ioctl(fd, cdb, ioa->qac_data->u.buf, length, SG_DXFER_TO_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0) ioa_cmd_err(ioa, &sense_data, "Resync Array", rc); close(fd); return rc; } /** * ipr_add_array_device - * @ioa: ipr ioa struct * @fd: device/file descriptor * @qac_data: query array configuration info * * Returns: * 0 if success / non-zero on failure **/ int ipr_add_array_device(struct ipr_ioa *ioa, int fd, struct ipr_array_query_data *qac_data) { u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; int length = qac_data->resp_len; ipr_update_qac_with_zeroed_devs(ioa); memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_ADD_ARRAY_DEVICE; cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; if (ioa->sis64) { cdb[10] = (length & 0xff000000) >> 24; cdb[11] = (length & 0xff0000) >> 16; } rc = sg_ioctl(fd, cdb, qac_data->u.buf, length, SG_DXFER_TO_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0) ioa_cmd_err(ioa, &sense_data, "Add Array Device", rc); return rc; } /** * ipr_query_command_status - * @dev: ipr dev struct * @buff: data buffer * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_command_status(struct ipr_dev *dev, void *buff) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int length = sizeof(struct ipr_cmd_status); char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_QUERY_COMMAND_STATUS; cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_FROM_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if ((rc != 0) && (errno != EINVAL || ipr_debug)) scsi_cmd_err(dev, &sense_data, "Query Command Status", rc); close(fd); return rc; } /** * ipr_query_resource_state * @dev: ipr dev struct * @buff: data buffer * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_resource_state(struct ipr_dev *dev, void *buff) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int length = sizeof(struct ipr_query_res_state); char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_QUERY_RESOURCE_STATE; cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_FROM_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Query Resource State", rc); close(fd); return rc; } /** * ipr_mod_sense - issue a mode sense command * @dev: ipr dev struct * @page: mode page * @buff: data buffer * * Returns: * 0 if success / non-zero on failure **/ int ipr_mode_sense(struct ipr_dev *dev, u8 page, void *buff) { struct sense_data_t sense_data; int rc; memset(&sense_data, 0, sizeof(sense_data)); rc = mode_sense(dev, page, buff, &sense_data); /* post log if error unless error is device format in progress */ if ((rc != 0) && (rc != ENOENT) && (((sense_data.error_code & 0x7F) != 0x70) || ((sense_data.sense_key & 0x0F) != 0x02) || /* NOT READY */ (sense_data.add_sense_code != 0x04) || /* LOGICAL UNIT NOT READY */ (sense_data.add_sense_code_qual != 0x04))) /* FORMAT IN PROGRESS */ scsi_cmd_err(dev, &sense_data, "Mode Sense", rc); return rc; } /** * ipr_log_sense - issue a log sense command * @dev: ipr dev struct * @page: mode page * @buff: data buffer * @length: buffer length * * Returns: * 0 if success / non-zero on failure **/ int ipr_log_sense(struct ipr_dev *dev, u8 page, void *buff, u16 length) { struct sense_data_t sense_data; u8 cdb[IPR_CCB_CDB_LEN]; int fd, rc; char *name = dev->gen_name; memset(&sense_data, 0, sizeof(sense_data)); if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { scsi_dbg(dev, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = LOG_SENSE; cdb[2] = 0x40 | page; cdb[7] = (length >> 8) & 0xff; cdb[8] = length & 0xff; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); scsi_cmd_dbg(dev, &sense_data, cdb, rc); close(fd); return rc; } /** * ipr_is_log_page_supported - is the give page supported? * @dev: ipr dev struct * @page: mode page * * Returns: * 1 if supported / 0 if not supported **/ int ipr_is_log_page_supported(struct ipr_dev *dev, u8 page) { struct ipr_supp_log_pages pages; int rc, i; memset(&pages, 0, sizeof(pages)); rc = ipr_log_sense(dev, 0, &pages, sizeof(pages)); if (rc) return -EIO; for (i = 0; i < ntohs(pages.length); i++) { if (pages.page[i] == page) return 1; } return 0; } /** * ipr_get_blk_size - return the block size for the given device * @dev: ipr dev struct * * Returns: * block size if success / non-zero on failure **/ int ipr_get_blk_size(struct ipr_dev *dev) { struct ipr_mode_pages mode_pages; struct ipr_block_desc *block_desc; int rc; rc = ipr_mode_sense(dev, 0, &mode_pages); if (rc) return rc; if (mode_pages.hdr.block_desc_len > 0) { block_desc = (struct ipr_block_desc *)mode_pages.data; return ((block_desc->block_length[1] << 8) | block_desc->block_length[2]); } return 0; } /** * ipr_mode_select - issue a mode select command * @dev: ipr dev struct * @buff: data buffer * @length: length of buffer * * Returns: * 0 if success / non-zero on failure **/ int ipr_mode_select(struct ipr_dev *dev, void *buff, int length) { int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = MODE_SELECT; cdb[1] = 0x10; /* PF = 1, SP = 0 */ cdb[4] = length; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_TO_DEV, &sense_data, IPR_INTERNAL_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Mode Select", rc); close(fd); return rc; } /** * page0x0a_setup - Start array protection for an array * @dev: ipr dev struct * * Returns: * 1 if success / 0 on failure **/ int page0x0a_setup(struct ipr_dev *dev) { struct ipr_mode_pages mode_pages; struct ipr_control_mode_page *page; memset(&mode_pages, 0, sizeof(mode_pages)); if (ipr_mode_sense(dev, 0x0A, &mode_pages)) return -EIO; page = (struct ipr_control_mode_page *)(((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); if (page->queue_algorithm_modifier != 1) return 0; if (page->tst == 1 && page->qerr != 3) return 0; if (page->qerr != 1) return 0; if (page->dque != 0) return 0; return 1; } /** * ipr_setup_af_qdepth - * @dev: ipr dev struct * @qdepth: queue depth * * Returns: * 0 if success / non-zero on failure **/ static int ipr_setup_af_qdepth(struct ipr_dev *dev, int qdepth) { struct ipr_mode_pages mode_pages; struct ipr_ioa_mode_page *page; int len; memset(&mode_pages, 0, sizeof(mode_pages)); if (ipr_mode_sense(dev, 0x20, &mode_pages)) return -EIO; page = (struct ipr_ioa_mode_page *) (((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); page->max_tcq_depth = qdepth; len = mode_pages.hdr.length + 1; mode_pages.hdr.length = 0; mode_pages.hdr.medium_type = 0; mode_pages.hdr.device_spec_parms = 0; page->hdr.parms_saveable = 0; if (ipr_mode_select(dev, &mode_pages, len)) return -EIO; return 0; } /** * ipr_reset_device - issue a SG_SCSI_RESET to a device * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_reset_device(struct ipr_dev *dev) { int fd, rc, arg; if (strlen(dev->gen_name) == 0) return -ENOENT; fd = open(dev->gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", dev->gen_name); return errno; } arg = SG_SCSI_RESET_DEVICE; rc = ioctl(fd, SG_SCSI_RESET, &arg); if (rc != 0) scsi_err(dev, "Reset Device failed. %m\n"); close(fd); return rc; } /** * ipr_re_read_partition - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_re_read_partition(struct ipr_dev *dev) { int fd, rc; if (strlen(dev->dev_name) == 0) return -ENOENT; fd = open(dev->dev_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", dev->gen_name); return errno; } rc = ioctl(fd, BLKRRPART, NULL); if (rc != 0) scsi_err(dev, "Re-read partition table failed. %m\n"); close(fd); return rc; } /** * ipr_read_capacity - issue a read capacity command to a device * @dev: ipr dev struct * @buff: data buffer * * Returns: * 0 if success / non-zero on failure **/ int ipr_read_capacity(struct ipr_dev *dev, void *buff) { int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; int length = sizeof(struct ipr_read_cap); char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = READ_CAPACITY; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Read Capacity", rc); close(fd); return rc; } /** * ipr_read_capacity_16 - issue a read capacity 16 to a device * @dev: ipr dev struct * @buff: data buffer * * Returns: * 0 if success / non-zero on failure **/ int ipr_read_capacity_16(struct ipr_dev *dev, void *buff) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int length = sizeof(struct ipr_read_cap16); char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_SERVICE_ACTION_IN; cdb[1] = IPR_READ_CAPACITY_16; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Read Capacity", rc); close(fd); return rc; } /** * ipr_reclaim_cache_store - * @ioa: ipr ioa struct * @action: action value * @buff: data buffer * * Returns: * 0 if success / non-zero on failure **/ int ipr_reclaim_cache_store(struct ipr_ioa *ioa, int action, void *buff) { char *file = ioa->ioa.gen_name; int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int timeout, rc; int length = sizeof(struct ipr_reclaim_query_data); if (strlen(file) == 0) return -ENOENT; fd = open(file, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n",file); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_RECLAIM_CACHE_STORE; cdb[1] = (u8)action; cdb[7] = (length & 0xff00) >> 8; cdb[8] = length & 0xff; if ((action & IPR_RECLAIM_ACTION) == IPR_RECLAIM_PERFORM) timeout = IPR_CACHE_RECLAIM_TIMEOUT; else timeout = IPR_INTERNAL_DEV_TIMEOUT; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_FROM_DEV, &sense_data, timeout); if (rc != 0) { if (sense_data.sense_key != ILLEGAL_REQUEST) ioa_cmd_err(ioa, &sense_data, "Reclaim Cache", rc); } else if ((action & IPR_RECLAIM_ACTION) == IPR_RECLAIM_PERFORM) ipr_reset_adapter(ioa); close(fd); return rc; } /** * ipr_start_stop - * @dev: ipr dev struct * @start: start or stop flag * @cmd: command buffer * * Returns: * 0 if success / non-zero on failure **/ static int ipr_start_stop(struct ipr_dev *dev, u8 start, char *cmd) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = START_STOP; cdb[4] = start; rc = sg_ioctl(fd, cdb, NULL, 0, SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, cmd, rc); close(fd); return rc; } /** * ipr_start_stop_start - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_start_stop_start(struct ipr_dev *dev) { return ipr_start_stop(dev, IPR_START_STOP_START, "Start Unit"); } /** * ipr_check_allow_restart - check the allow_restart flag for a device * @dev: ipr dev struct * * Return value: * none **/ int ipr_check_allow_restart(struct ipr_dev *dev) { char path[PATH_MAX]; char value[8]; ssize_t len; sprintf(path,"/sys/class/scsi_disk/%s", dev->scsi_dev_data->sysfs_device_name); len = sysfs_read_attr(path, "allow_restart", value, 8); if (len < 1) { syslog_dbg("Failed to open allow_restart parameter.\n"); return -1; } return atoi(value); } /** * ipr_allow_restart - set or clear the allow_restart flag for a device * @dev: ipr dev struct * @allow: value to set * * Return value: * none **/ void ipr_allow_restart(struct ipr_dev *dev, int allow) { char path[PATH_MAX]; char value_str[8]; ssize_t len; sprintf(path,"/sys/class/scsi_disk/%s", dev->scsi_dev_data->sysfs_device_name); len = sysfs_read_attr(path, "allow_restart", value_str, 8); if (len < 1) { syslog_dbg("Failed to open allow_restart parameter.\n"); return; } if (atoi(value_str) == allow) { return; } snprintf(value_str, 8, "%d", allow); if (sysfs_write_attr(path, "allow_restart", value_str, 1) < 0) syslog_dbg("Failed to write allow_restart parameter.\n"); } /** * ipr_start_stop_stop - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_start_stop_stop(struct ipr_dev *dev) { return ipr_start_stop(dev, IPR_START_STOP_STOP, "Stop Unit"); } /** * __ipr_test_unit_ready - issue a test unit ready command * @dev: ipr dev struct * @sense_data: sense data struct * * Returns: * 0 if success / non-zero on failure **/ int __ipr_test_unit_ready(struct ipr_dev *dev, struct sense_data_t *sense_data) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; int length = 0, allow_restart = 0; char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = TEST_UNIT_READY; rc = sg_ioctl(fd, cdb, NULL, length, SG_DXFER_FROM_DEV, sense_data, IPR_INTERNAL_DEV_TIMEOUT); close(fd); if (rc && sense_data->sense_key == NOT_READY && sense_data->add_sense_code == 0x4 && sense_data->add_sense_code_qual == 0x2) { allow_restart = ipr_check_allow_restart(dev); if (allow_restart) ipr_start_stop(dev, IPR_START_STOP_START, "Start Unit"); } return rc; } /** * ipr_test_unit_ready - issue a test unit ready command * @dev: ipr dev struct * @sense_data: sense data struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_test_unit_ready(struct ipr_dev *dev, struct sense_data_t *sense_data) { int rc = __ipr_test_unit_ready(dev, sense_data); if (rc != 0 && sense_data->sense_key != NOT_READY && sense_data->sense_key != UNIT_ATTENTION && (!strcmp(tool_name, "iprconfig") || ipr_debug)) scsi_cmd_err(dev, sense_data, "Test Unit Ready", rc); return rc; } static struct ipr_dev *find_multipath_jbod(struct ipr_dev *dev) { struct ipr_ioa *ioa; struct ipr_dev *multipath_dev; for_each_sas_ioa(ioa) { if (ioa == dev->ioa) continue; for_each_dev(ioa, multipath_dev) { if (dev->scsi_dev_data->device_id == multipath_dev->scsi_dev_data->device_id) return multipath_dev; } } return NULL; } /** * ipr_format_unit - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_format_unit(struct ipr_dev *dev) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; u8 *defect_list_hdr; int length = IPR_DEFECT_LIST_HDR_LEN; char *name = dev->gen_name; char cmnd[1000]; struct ipr_dev *multipath_dev; if (strlen(dev->dev_name) && dev->scsi_dev_data->device_id) { sprintf(cmnd, "/sbin/multipathd -k\"del path %s\" &> /dev/null", strrchr(dev->dev_name, '/') + 1); system(cmnd); sprintf(cmnd, "/bin/rm -rf %s %s%s", dev->dev_name, dev->dev_name, "[0-9]*"); system(cmnd); multipath_dev = find_multipath_jbod(dev); if (multipath_dev) { sprintf(cmnd, "/sbin/multipathd -k\"del path %s\" &> /dev/null", strrchr(multipath_dev->dev_name, '/') + 1); system(cmnd); sprintf(cmnd, "/bin/rm -rf %s %s%s", multipath_dev->dev_name, multipath_dev->dev_name , "[0-9]*"); system(cmnd); } system("/sbin/multipath -F &> /dev/null"); } if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); defect_list_hdr = calloc(1, IPR_DEFECT_LIST_HDR_LEN); cdb[0] = FORMAT_UNIT; cdb[1] = IPR_FORMAT_DATA; /* lun = 0, fmtdata = 1, cmplst = 0, defect list format = 0 */ cdb[4] = 1; defect_list_hdr[1] = IPR_FORMAT_IMMED; /* FOV = 0, DPRY = 0, DCRT = 0, STPF = 0, IP = 0, DSP = 0, Immed = 1, VS = 0 */ rc = sg_ioctl(fd, cdb, defect_list_hdr, length, SG_DXFER_TO_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); free(defect_list_hdr); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Format Unit", rc); close(fd); return rc; } /** * ipr_evaluate_device - * @dev: ipr dev struct * @res_handle: resource handle * * Returns: * 0 if success / non-zero on failure **/ int ipr_evaluate_device(struct ipr_dev *dev, u32 res_handle) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; u32 resource_handle; int length = 0; char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); resource_handle = ntohl(res_handle); cdb[0] = IPR_EVALUATE_DEVICE; cdb[2] = (u8)(resource_handle >> 24); cdb[3] = (u8)(resource_handle >> 16); cdb[4] = (u8)(resource_handle >> 8); cdb[5] = (u8)(resource_handle); rc = sg_ioctl(fd, cdb, NULL, length, SG_DXFER_FROM_DEV, &sense_data, IPR_EVALUATE_DEVICE_TIMEOUT); if (rc != 0 && sense_data.sense_key != ILLEGAL_REQUEST) scsi_cmd_err(dev, &sense_data, "Evaluate Device Capabilities", rc); close(fd); return rc; } /** * ipr_disrupt_device - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_disrupt_device(struct ipr_dev *dev) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; u32 res_handle; char *name = dev->ioa->ioa.gen_name; if (strlen(name) == 0) return -ENOENT; if (!dev->dev_rcd) return -EINVAL; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); res_handle = ntohl(dev->resource_handle); cdb[0] = IPR_IOA_SERVICE_ACTION; cdb[1] = IPR_DISRUPT_DEVICE; if (!ipr_force) cdb[2] = 0x40; cdb[6] = (u8)(res_handle >> 24); cdb[7] = (u8)(res_handle >> 16); cdb[8] = (u8)(res_handle >> 8); cdb[9] = (u8)(res_handle); scsi_info(dev, "Attempting to force device failure " "with disrupt device command\n"); rc = sg_ioctl(fd, cdb, NULL, 0, SG_DXFER_NONE, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0 && sense_data.sense_key != ILLEGAL_REQUEST) scsi_cmd_err(dev, &sense_data, "Disrupt Device", rc); close(fd); return rc; } /** * ipr_inquiry - issue an inquiry command * @dev: ipr dev struct * @page: mode page * @buff: data buffer * @length buffer length * * Returns: * 0 if success / non-zero on failure **/ int ipr_inquiry(struct ipr_dev *dev, u8 page, void *buff, u8 length) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = INQUIRY; if (page != IPR_STD_INQUIRY) { cdb[1] = 0x01; cdb[2] = page; } cdb[4] = length; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0 && (ipr_debug || sense_data.sense_key != ILLEGAL_REQUEST)) scsi_cmd_err(dev, &sense_data, "Inquiry", rc); close(fd); return rc; } /** * __ipr_get_fw_version - get the firmware version * @dev: ipr dev struct * @pag3_ing: page 3 inquiry data * @release_level: location to copy version info into * * Returns: * nothing **/ static void __ipr_get_fw_version(struct ipr_dev *dev, struct ipr_dasd_inquiry_page3 *page3_inq, u8 release_level[4]) { if (ipr_is_ses(dev) && !__ioa_is_spi(dev->ioa)) memcpy(release_level, page3_inq->load_id, 4); else memcpy(release_level, page3_inq->release_level, 4); } /** * ipr_get_fw_version - get the firmware version entry point * @dev: ipr dev struct * @release_level: location to copy version info into * * Returns: * 0 if success / non-zero on failure **/ int ipr_get_fw_version(struct ipr_dev *dev, u8 release_level[4]) { struct ipr_dasd_inquiry_page3 page3_inq; int rc; memset(&page3_inq, 0, sizeof(page3_inq)); rc = ipr_inquiry(dev, 3, &page3_inq, sizeof(page3_inq)); if (rc) return rc; __ipr_get_fw_version(dev, &page3_inq, release_level); return 0; } /** * ipr_init_res_addr_aliases - * @aliases: ipr_res_addr_aliases struct * @res_addr: ipr_res_addr struct * * Returns: * nothing **/ static void ipr_init_res_addr_aliases(struct ipr_res_addr_aliases *aliases, struct ipr_res_addr *res_addr) { struct ipr_res_addr *ra; aliases->length = htonl(sizeof(*res_addr) * IPR_DEV_MAX_PATHS); for_each_ra_alias(ra, aliases) memcpy(ra, res_addr, sizeof(*ra)); } /** * ipr_query_res_addr_aliases - * @ioa: ipr ioa struct * @res_addr: ipr_res_addr struct * @aliases: ipr_res_addr_aliases struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_res_addr_aliases(struct ipr_ioa *ioa, struct ipr_res_addr *res_addr, struct ipr_res_addr_aliases *aliases) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; char *name = ioa->ioa.gen_name; struct ipr_res_addr *ra; ipr_init_res_addr_aliases(aliases, res_addr); for_each_ra_alias(ra, aliases) ra->host = ioa->host_num; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_IOA_SERVICE_ACTION; cdb[1] = IPR_QUERY_RES_ADDR_ALIASES; cdb[3] = res_addr->bus; cdb[4] = res_addr->target; cdb[5] = res_addr->lun; cdb[10] = sizeof(*aliases) >> 24; cdb[11] = (sizeof(*aliases) >> 16) & 0xff; cdb[12] = (sizeof(*aliases) >> 8) & 0xff; cdb[13] = sizeof(*aliases) & 0xff; rc = sg_ioctl(fd, cdb, aliases, sizeof(*aliases), SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc || (ntohl(aliases->length) <= 4)) ipr_init_res_addr_aliases(aliases, res_addr); if (rc != 0 && sense_data.sense_key != ILLEGAL_REQUEST) ioa_cmd_err(ioa, &sense_data, "Query Resource Address Aliases", rc); if (rc && sense_data.sense_key != ILLEGAL_REQUEST) rc = 0; for_each_ra_alias(ra, aliases) ra->host = ioa->host_num; close(fd); return rc; } /** * ipr_init_res_path_aliases - * @aliases: ipr_res_path_aliases struct * @res_addr: ipr_res_path * * Returns: * nothing **/ static void ipr_init_res_path_aliases(struct ipr_res_path_aliases *aliases, struct ipr_res_path *res_path) { struct ipr_res_path *rp; aliases->length = htonl(sizeof(*res_path) * IPR_DEV_MAX_PATHS); for_each_rp_alias(rp, aliases) { memcpy(rp, res_path, sizeof(*rp)); } } /** * ipr_query_res_path_aliases - * @ioa: ipr ioa struct * @res_addr: ipr_res_path struct * @aliases: ipr_res_ipr_aliases struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_res_path_aliases(struct ipr_ioa *ioa, struct ipr_res_path *res_path, struct ipr_res_path_aliases *aliases) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; char *name = ioa->ioa.gen_name; ipr_init_res_path_aliases(aliases, res_path); if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_IOA_SERVICE_ACTION; cdb[1] = IPR_QUERY_RES_PATH_ALIASES; memcpy(&cdb[2], res_path->res_path_bytes, sizeof(struct ipr_res_path)); cdb[10] = sizeof(*aliases) >> 24; cdb[11] = (sizeof(*aliases) >> 16) & 0xff; cdb[12] = (sizeof(*aliases) >> 8) & 0xff; cdb[13] = sizeof(*aliases) & 0xff; rc = sg_ioctl(fd, cdb, aliases, sizeof(*aliases), SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc || (ntohl(aliases->length) <= 8)) ipr_init_res_path_aliases(aliases, res_path); if (rc != 0 && sense_data.sense_key != ILLEGAL_REQUEST) ioa_cmd_err(ioa, &sense_data, "Query Resource Path Aliases", rc); if (rc && sense_data.sense_key != ILLEGAL_REQUEST) rc = 0; close(fd); return rc; } /** * ipr_query_sas_expander_info - * @ioa: ipr ioa struct * @info: ipr_query_sas_expander_info struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_sas_expander_info(struct ipr_ioa *ioa, struct ipr_query_sas_expander_info *info) { char *name = ioa->ioa.gen_name; struct sense_data_t sense_data; u8 cdb[IPR_CCB_CDB_LEN]; int fd, rc; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_IOA_SERVICE_ACTION; cdb[1] = IPR_QUERY_SAS_EXPANDER_INFO; cdb[10] = sizeof(*info) >> 24; cdb[11] = (sizeof(*info) >> 16) & 0xff; cdb[12] = (sizeof(*info) >> 8) & 0xff; cdb[13] = sizeof(*info) & 0xff; rc = sg_ioctl(fd, cdb, info, sizeof(*info), SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0 && sense_data.sense_key != ILLEGAL_REQUEST) ioa_cmd_err(ioa, &sense_data, "Query SAS Expander Info", rc); close(fd); return rc; } /** * ipr_change_cache_parameters * @ioa: ipr ioa struct * @mode: caching mode to set * * Returns: * 0 if success / non-zero on failure **/ int ipr_change_cache_parameters(struct ipr_ioa *ioa, int mode) { char *name = ioa->ioa.gen_name; struct sense_data_t sense_data; u8 cdb[IPR_CCB_CDB_LEN]; int fd, rc; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_IOA_SERVICE_ACTION; cdb[1] = IPR_CHANGE_CACHE_PARAMETERS; cdb[2] = mode; rc = sg_ioctl(fd, cdb, NULL, 0, SG_DXFER_NONE, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0 && (sense_data.sense_key != ILLEGAL_REQUEST || ipr_debug)) ioa_cmd_err(ioa, &sense_data, "change cache params failed", rc); close(fd); return rc; } /** * ipr_query_cache_parameters - * @ioa: ipr ioa struct * @buf: buffer for returned cache data * @len: length of buffer * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_cache_parameters(struct ipr_ioa *ioa, void *buf, int len) { char *name = ioa->ioa.gen_name; struct sense_data_t sense_data; u8 cdb[IPR_CCB_CDB_LEN]; int fd, rc; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_IOA_SERVICE_ACTION; cdb[1] = IPR_QUERY_CACHE_PARAMETERS; cdb[10] = len >> 24; cdb[11] = len >> 16 & 0xff; cdb[12] = len >> 8 & 0xff; cdb[13] = len & 0xff; rc = sg_ioctl(fd, cdb, buf, len, SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0 && (sense_data.sense_key != ILLEGAL_REQUEST || ipr_debug)) ioa_cmd_err(ioa, &sense_data, "Query Cache Parameters", rc); close(fd); return rc; } /** * ipr_query_res_redundancy_info - * @dev: ipr dev struct * @info: ipr_res_redundancy_info struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_res_redundancy_info(struct ipr_dev *dev, struct ipr_res_redundancy_info *info) { struct scsi_dev_data *scsi_dev_data = dev->scsi_dev_data; char *name = dev->ioa->ioa.gen_name; struct ipr_dev_record *dev_record; struct sense_data_t sense_data; u8 cdb[IPR_CCB_CDB_LEN]; u32 res_handle; int fd, rc; if (scsi_dev_data) res_handle = scsi_dev_data->handle; else if (ipr_is_af_dasd_device(dev)) { dev_record = dev->dev_rcd; if (dev_record->no_cfgte_dev) return -EINVAL; res_handle = ntohl(dev->resource_handle); } else return -EINVAL; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_IOA_SERVICE_ACTION; if (dev->ioa->sis64) cdb[1] = IPR_QUERY_RES_REDUNDANCY_INFO64; else cdb[1] = IPR_QUERY_RES_REDUNDANCY_INFO; cdb[2] = (u8)(res_handle >> 24); cdb[3] = (u8)(res_handle >> 16); cdb[4] = (u8)(res_handle >> 8); cdb[5] = (u8)(res_handle); cdb[10] = sizeof(*info) >> 24; cdb[11] = (sizeof(*info) >> 16) & 0xff; cdb[12] = (sizeof(*info) >> 8) & 0xff; cdb[13] = sizeof(*info) & 0xff; rc = sg_ioctl(fd, cdb, info, sizeof(*info), SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0 && sense_data.sense_key != ILLEGAL_REQUEST) ioa_cmd_err(dev->ioa, &sense_data, "Query Resource Redundancy Info", rc); close(fd); return rc; } /** * get_livedump_fname - * @ioa: ipr_ioa struct * @fname: string to return the filename * @max: max size of fname * * Returns: * negative on failure / otherwise length of the file name * **/ static int get_livedump_fname(struct ipr_ioa *ioa, char *fname, int max) { time_t cur_time, rc; struct tm *cur_tm; char time_str[100]; int len; fname[0] = '\0'; rc = time(&cur_time); if (rc == ((time_t)-1)) return -EIO; cur_tm = localtime(&cur_time); if (!cur_tm) return -EIO; strftime(time_str, sizeof(time_str), "%Y%m%d.%H%M", cur_tm); len = snprintf(fname, max, "ipr-%04X-%s-dump-%s", ioa->ccin, ioa->pci_address, time_str); return len; } #define IPR_MAX_LIVE_DUMP_SIZE (16 * 1024 * 1024) /** * get_scsi_max_xfer_len - * @fd: IOA sg file descriptor * * Returns: * -1 on failure / otherwise max scatter-gather transfer length * **/ static int get_scsi_max_xfer_len(int fd) { int rc, max_tablesize, max_scsi_len; char elem_str[64]; ssize_t len; len = sysfs_read_attr("/sys/module/sg/parameters", "scatter_elem_sz", elem_str, 64); if (len < 0) { syslog_dbg("Failed to read scatter_elem_sz parameter from sg module. %m\n"); return -1; } rc = ioctl(fd, SG_GET_SG_TABLESIZE, &max_tablesize); if (rc) { syslog_dbg("Unable to get device scatter-gather table size. %m\n"); return -1; } max_scsi_len = max_tablesize * atoi(elem_str); if (max_scsi_len < IPR_MAX_LIVE_DUMP_SIZE) return max_scsi_len; else return IPR_MAX_LIVE_DUMP_SIZE; } /** * ipr_get_live_dump - * @dev: ipr dev struct * @info: ipr_res_redundancy_info struct * * Returns: * 0 if success / non-zero on failure * **/ int ipr_get_live_dump(struct ipr_ioa *ioa) { char *name = ioa->ioa.gen_name; struct sense_data_t sense_data; u8 cdb[IPR_CCB_CDB_LEN], *buf; char dump_file[100], dump_path[100]; int rc, fd, fd_dump, buff_len; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } buff_len = get_scsi_max_xfer_len(fd); if (buff_len <= 0) { rc = -EPERM; goto out; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_IOA_SERVICE_ACTION; cdb[1] = IPR_LIVE_DUMP; cdb[10] = buff_len >> 24; cdb[11] = (buff_len >> 16) & 0xff; cdb[12] = (buff_len >> 8) & 0xff; cdb[13] = buff_len & 0xff; buf = (u8*) malloc(buff_len); rc = sg_ioctl(fd, cdb, buf, buff_len, SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc) { ioa_cmd_err(ioa, &sense_data, "Live Dump", rc); goto out_free; } rc = get_livedump_fname(ioa, dump_file, sizeof(dump_file)); if (rc < 0) { syslog_dbg("Failed to create a live dump file name.\n"); goto out_free; } snprintf(dump_path, sizeof(dump_path), "%s%s", IPRDUMP_DIR, dump_file); fd_dump = creat(dump_path, S_IRUSR); if (fd_dump < 0) { syslog(LOG_ERR, "Could not open %s. %m\n", dump_path); rc = fd_dump; goto out_free; } rc = write(fd_dump, buf, buff_len); if (rc != buff_len) syslog(LOG_ERR, "Could not write dump on file %s. %m\n", dump_path); close(fd_dump); out_free: free(buf); out: close(fd); return rc; } /** * ipr_set_array_asym_access - * @dev: ipr_dev struct * @qac: ipr_array_query_data struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_set_array_asym_access(struct ipr_ioa *ioa) { char *name = ioa->ioa.gen_name; struct sense_data_t sense_data; u8 cdb[IPR_CCB_CDB_LEN]; int fd, rc; int length = ioa->qac_data->resp_len; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_SET_ARRAY_ASYMMETRIC_ACCESS; cdb[7] = (length >> 8) & 0xff; cdb[8] = length & 0xff; if (ioa->sis64) { cdb[10] = (length & 0xff000000) >> 24; cdb[11] = (length & 0xff0000) >> 16; } rc = sg_ioctl(fd, cdb, ioa->qac_data->u.buf, length, SG_DXFER_TO_DEV, &sense_data, IPR_ARRAY_CMD_TIMEOUT); if (rc != 0 && (sense_data.sense_key != ILLEGAL_REQUEST || ipr_debug)) ioa_cmd_err(ioa, &sense_data, "Set Array Asymmetric Access", rc); close(fd); return rc; } /** * get_write_buffer_dev - FIXME - probably need to remove this routine. * @dev: ipr dev struct * * Returns: * device generic name for now - see FIXME below **/ #if 0 static char *get_write_buffer_dev(struct ipr_dev *dev) { struct sysfs_class_device *class_device; struct sysfs_attribute *dev_attr; int rc, val; char path[SYSFS_PATH_MAX]; /* xxx FIXME */ return dev->gen_name; if (!strlen(dev->dev_name) || !dev->scsi_dev_data) return dev->gen_name; class_device = sysfs_open_class_device("block", &dev->dev_name[5]); if (!class_device) { scsi_dbg(dev, "Failed to open block class device for %s. %m\n", dev->dev_name); return dev->gen_name; } sprintf(path, "%s/queue/max_sectors_kb", class_device->path); dev_attr = sysfs_open_attribute(path); if (!dev_attr) { scsi_dbg(dev, "Failed to get max_sectors_kb attribute. %m\n"); goto fail; } rc = sysfs_read_attribute(dev_attr); if (rc) { sysfs_close_attribute(dev_attr); scsi_dbg(dev, "Failed to read max_sectors_kb attribute. %m\n"); goto fail; } sscanf(dev_attr->value, "%d", &val); sysfs_close_attribute(dev_attr); sysfs_close_class_device(class_device); if (val > 256) return dev->dev_name; return dev->gen_name; fail: sysfs_close_class_device(class_device); return dev->gen_name; } #endif /** * ipr_resume_device_bus - * @ioa: ipr ioa struct * @res_addr: ipr_res_addr struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_resume_device_bus(struct ipr_dev *dev, struct ipr_res_addr *res_addr) { int fd, rc, cdb_num; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int length = 0; struct ipr_ioa *ioa = dev->ioa; char *rp, *endptr; if (strlen(ioa->ioa.gen_name) == 0) return -ENOENT; fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", ioa->ioa.gen_name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_RESUME_DEV_BUS; cdb[1] = IPR_RDB_UNQUIESCE_AND_REBALANCE; if (ioa->sis64) { /* convert res path string to bytes */ cdb_num = 2; rp = dev->res_path_name; do { cdb[cdb_num++] = (u8)strtol(rp, &endptr, 16); rp = endptr+1; } while (*endptr != '\0' && cdb_num < 10); while (cdb_num < 10) cdb[cdb_num++] = 0xff; } else { cdb[3] = res_addr->bus; cdb[4] = res_addr->target; cdb[5] = res_addr->lun; } rc = sg_ioctl(fd, cdb, NULL, length, SG_DXFER_FROM_DEV, &sense_data, IPR_SUSPEND_DEV_BUS_TIMEOUT); close(fd); return rc; } /** * __ipr_write_buffer - Issue WRITE_BUFFER command. * In older kernels, max_sectors_kb is not big enough and * would return -EIO when using dev_name to issue * SG_IO ioctl(). In that case, we have to use gen_name. * Also, in case dev_name does not exist, we need to use * gen_name. Hence, we try dev_name first and switch to * gen_name if we get -ENOENT or -EIO. * @dev: ipr dev struct * @mode: mode value * @buff: data buffer * @length: buffer length * @sense_data: sense data struct * * Returns: * 0 if success / non-zero on failure **/ static int __ipr_write_buffer(struct ipr_dev *dev, u8 mode, void *buff, int length, struct sense_data_t *sense_data) { u32 direction = length ? SG_DXFER_TO_DEV : SG_DXFER_NONE; u8 cdb[IPR_CCB_CDB_LEN]; int rc; ENTER; if ((strlen(dev->dev_name) == 0) && (strlen(dev->gen_name) == 0)) return -ENOENT; memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = WRITE_BUFFER; cdb[1] = mode; cdb[6] = (length & 0xff0000) >> 16; cdb[7] = (length & 0x00ff00) >> 8; cdb[8] = length & 0x0000ff; rc = sg_ioctl_by_name(dev->dev_name, cdb, buff, length, direction, sense_data, IPR_WRITE_BUFFER_TIMEOUT); if (rc != 0) { if ((rc == -ENOENT) || (rc == -EIO) || (rc == -EINVAL)) { syslog_dbg("Write buffer failed on sd device %s. %m\n", dev->dev_name); rc = sg_ioctl_by_name(dev->gen_name, cdb, buff, length, direction, sense_data, IPR_WRITE_BUFFER_TIMEOUT); if (rc != 0) { scsi_cmd_err(dev, sense_data, "Write buffer using sg device", rc); if (rc == -ENOMEM) syslog(LOG_ERR, "Cannot get enough memory to " "perform microcode download " "through %s. Reduce system " "memory usage and try again.\n", dev->gen_name); } } else scsi_cmd_err(dev, sense_data, "Write buffer using sd device", rc); } return rc; } /** * ipr_write_buffer - * @dev: ipr dev struct * @buff: data buffer * @length: buffer length * * Returns: * 0 if success / non-zero on failure **/ int ipr_write_buffer(struct ipr_dev *dev, void *buff, int length) { int rc, i; struct sense_data_t sense_data; struct ipr_disk_attr attr; int old_qdepth; int sas_ses = 0; int sas_ses_retries = 5; int mode5 = 1; ENTER; if ((rc = ipr_get_dev_attr(dev, &attr))) { scsi_dbg(dev, "Failed to get device attributes. rc=%d\n", rc); return rc; } if (ipr_is_ses(dev) && !__ioa_is_spi(dev->ioa)) sas_ses = 1; if (sas_ses && !ipr_mode5_write_buffer) mode5 = 0; /* * Set the queue depth to 1 for the duration of the code download. * This prevents us from getting I/O errors during the code update */ old_qdepth = attr.queue_depth; attr.queue_depth = 1; if ((rc = ipr_set_dev_attr(dev, &attr, 0))) { scsi_dbg(dev, "Failed to set queue depth to 1. rc=%d\n", rc); return rc; } if (sas_ses) { if (!mode5) { for (i = 0; i < (sas_ses_retries + 1); i++) { rc = __ipr_write_buffer(dev, 0x0e, buff, length, &sense_data); if (!rc) { break; } else if (rc && sense_data.sense_key == UNIT_ATTENTION && sense_data.add_sense_code == 0x29 && sense_data.add_sense_code_qual == 0x00) { continue; } else if (rc && sense_data.sense_key == ILLEGAL_REQUEST) { mode5 = 1; break; } else if (rc) { goto out; } } } rc = ipr_suspend_device_bus(dev, &dev->res_addr[0], IPR_SDB_CHECK_AND_QUIESCE_ENC); if (rc) { scsi_dbg(dev, "Failed to quiesce the SAS port.\n"); goto out; } for (i = 0; i < (sas_ses_retries + 1); i++) { if (mode5) rc = __ipr_write_buffer(dev, 5, buff, length, &sense_data); else rc = __ipr_write_buffer(dev, 0x0f, NULL, 0, &sense_data); if (rc && sense_data.sense_key == UNIT_ATTENTION && sense_data.add_sense_code == 0x29 && sense_data.add_sense_code_qual == 0x00) { continue; } else { break; } } if (rc) { scsi_dbg(dev, "Write buffer %sfailed.\n", mode5 ? "" : "activate "); goto out_resume; } } else { if ((rc = __ipr_write_buffer(dev, 5, buff, length, &sense_data))) goto out; } scsi_dbg(dev, "Waiting for device to come ready after download.\n"); sleep(sas_ses ? 120 : 5); for (i = 0, rc = -1; rc && (i < 60); i++, sleep(1)) rc = __ipr_test_unit_ready(dev, &sense_data); out_resume: if (sas_ses) ipr_resume_device_bus(dev, &dev->res_addr[0]); out: attr.queue_depth = old_qdepth; ipr_set_dev_attr(dev, &attr, 0); LEAVE; return rc; } /** * ipr_suspend_device_bus - * @ioa: ipr ioa struct * @res_addr: ipr_res_addr struct * @option: option value * * Returns: * 0 if success / non-zero on failure **/ int ipr_suspend_device_bus(struct ipr_dev *dev, struct ipr_res_addr *res_addr, u8 option) { int fd, rc, cdb_num; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int length = 0; struct ipr_ioa *ioa = dev->ioa; char *rp, *endptr; ENTER; if (strlen(ioa->ioa.gen_name) == 0) return -ENOENT; fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", ioa->ioa.gen_name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_SUSPEND_DEV_BUS; cdb[1] = option; if (ioa->sis64) { /* convert res path string to bytes */ cdb_num = 2; rp = dev->res_path_name; do { cdb[cdb_num++] = (u8)strtol(rp, &endptr, 16); rp = endptr+1; } while (*endptr != '\0' && cdb_num < 10); while (cdb_num < 10) cdb[cdb_num++] = 0xff; } else { cdb[3] = res_addr->bus; cdb[4] = res_addr->target; cdb[5] = res_addr->lun; } rc = sg_ioctl(fd, cdb, NULL, length, SG_DXFER_FROM_DEV, &sense_data, IPR_SUSPEND_DEV_BUS_TIMEOUT); close(fd); LEAVE; return rc; } /** * ipr_can_remove_device - indicate whether or not a device can be removed * @dev: ipr dev struct * * Returns: * 1 if device can be removed / 0 if device can not be removed **/ int ipr_can_remove_device(struct ipr_dev *dev) { struct ipr_res_addr *ra; for_each_ra(ra, dev) { if (ipr_suspend_device_bus(dev, ra, IPR_SDB_CHECK_ONLY)) return 0; } return 1; } /** * ipr_receive_diagnostics - * @dev: ipr dev struct * @page: page number * @buff: data buffer * @length: buffer length * * Returns: * 0 if success / non-zero on failure **/ int ipr_receive_diagnostics(struct ipr_dev *dev, u8 page, void *buff, int length) { int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = RECEIVE_DIAGNOSTIC; cdb[1] = 0x01; cdb[2] = page; cdb[3] = (length >> 8) & 0xff; cdb[4] = length & 0xff; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Receive diagnostics", rc); close(fd); return rc; } /** * ipr_send_diagnostics - * @dev: ipr dev struct * @buff: data buffer * @length: buffer length * * Returns: * 0 if success / non-zero on failure **/ int ipr_send_diagnostics(struct ipr_dev *dev, void *buff, int length) { int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; char *name = dev->gen_name; if (strlen(name) == 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = SEND_DIAGNOSTIC; cdb[1] = 0x10; cdb[3] = (length >> 8) & 0xff; cdb[4] = length & 0xff; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_TO_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Send diagnostics", rc); close(fd); return rc; } /** * set_supported_devs - * @dev: ipr dev struct * @std_inq: ipr_std_inq_data struct * * Returns: * 0 if success / non-zero on failure **/ static int set_supported_devs(struct ipr_dev *dev, struct ipr_std_inq_data *std_inq) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; struct ipr_supported_device supported_dev; fd = open(dev->ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", dev->ioa->ioa.gen_name); return errno; } memset(&supported_dev, 0, sizeof(struct ipr_supported_device)); memcpy(&supported_dev.vpids, &std_inq->vpids, sizeof(std_inq->vpids)); supported_dev.num_records = 1; supported_dev.data_length = htons(sizeof(supported_dev)); supported_dev.reserved = 0; memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = SET_SUPPORTED_DEVICES; cdb[7] = (sizeof(supported_dev) >> 8) & 0xff; cdb[8] = sizeof(supported_dev) & 0xff; rc = sg_ioctl(fd, cdb, &supported_dev, sizeof(supported_dev), SG_DXFER_TO_DEV, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0) ioa_cmd_err(dev->ioa, &sense_data, "Set supported devices", rc); close(fd); return rc; } /** * __device_supported - * @dev: ipr dev struct * @inq: ipr_std_inq_data * * Returns: * 0 if device is supported / 1 if device is not supported **/ static int __device_supported(struct ipr_dev *dev, struct ipr_std_inq_data *inq) { int i, j; const struct ses_table_entry *ste; if (!dev->scsi_dev_data) return 1; ste = get_ses_entry(dev->ioa, dev->scsi_dev_data->channel); if (!ste) return 1; if (!ste->block_15k_devices) return 1; for (i = 0; i < ARRAY_SIZE(unsupported_dasd); i++) { for (j = 0; j < IPR_VENDOR_ID_LEN; j++) { if (unsupported_dasd[i].compare_vendor_id_byte[j] && unsupported_dasd[i].vendor_id[j] != inq->vpids.vendor_id[j]) break; } if (j != IPR_VENDOR_ID_LEN) continue; for (j = 0; j < IPR_PROD_ID_LEN; j++) { if (unsupported_dasd[i].compare_product_id_byte[j] && unsupported_dasd[i].product_id[j] != inq->vpids.product_id[j]) break; } if (j != IPR_PROD_ID_LEN) continue; return 0; } return 1; } /** * device_supported - * @dev: ipr dev struct * * Returns: * 0 if device is supported / 1 if device is not supported **/ int device_supported(struct ipr_dev *dev) { struct ipr_std_inq_data std_inq; if (ipr_inquiry(dev, IPR_STD_INQUIRY, &std_inq, sizeof(std_inq))) return -EIO; return __device_supported(dev, &std_inq); } /** * enable_af - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ int enable_af(struct ipr_dev *dev) { struct ipr_std_inq_data std_inq; if (ipr_inquiry(dev, IPR_STD_INQUIRY, &std_inq, sizeof(std_inq))) return -EIO; if (!__device_supported(dev, &std_inq)) { scsi_dbg(dev, "Unsupported device attached\n"); return -EIO; } if (set_supported_devs(dev, &std_inq)) return -EIO; return 0; } /** * get_blk_size - return the block size of the device * @dev: ipr dev struct * * Returns: * block size / non-zero on failure **/ static int get_blk_size(struct ipr_dev *dev) { struct ipr_mode_pages modes; struct ipr_block_desc *block_desc; int rc; memset(&modes, 0, sizeof(modes)); rc = ipr_mode_sense(dev, 0x0a, &modes); if (!rc) { if (modes.hdr.block_desc_len > 0) { block_desc = (struct ipr_block_desc *)(modes.data); return ((block_desc->block_length[1] << 8) | block_desc->block_length[2]); } } return -EIO; } /** * format_req - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ int format_req(struct ipr_dev *dev) { struct sense_data_t sense_data; int rc; rc = ipr_test_unit_ready(dev, &sense_data); if (rc == CHECK_CONDITION && sense_data.add_sense_code == 0x31 && sense_data.add_sense_code_qual == 0x00) return 1; rc = get_blk_size(dev); if (rc < 0) return 0; if (ipr_is_gscsi(dev) && (rc != 512 || rc != 4096)) return 1; return 0; } /* * Scatter/gather list buffers are checked against the value returned * by queue_dma_alignment(), which defaults to 511 in Linux 2.6, * for alignment if a SG_IO ioctl request is sent through a /dev/sdX device. */ #define IPR_S_G_BUFF_ALIGNMENT 512 #define IPR_MAX_XFER 0x8000 const int cdb_size[] ={6, 10, 10, 0, 16, 12, 16, 16}; /** * _sg_ioctl - * @fd: file descriptor * @cdb: cdb * @data: data pointer * @xfer_len transfer length * @data_direction transfer to dev or from dev * @sense_data sense data pointer * @timeout_in_sec timeout value * @retries number of retries * * Returns: * 0 if success / non-zero on failure **/ static int _sg_ioctl(int fd, u8 cdb[IPR_CCB_CDB_LEN], void *data, u32 xfer_len, u32 data_direction, struct sense_data_t *sense_data, u32 timeout_in_sec, int retries) { int rc = 0; sg_io_hdr_t io_hdr_t; sg_iovec_t *iovec = NULL; int iovec_count = 0; int i; int buff_len, segment_size; void *dxferp; u8 *buf; /* check if scatter gather should be used */ if (xfer_len > IPR_MAX_XFER) { iovec_count = (xfer_len/IPR_MAX_XFER) + 1; iovec = malloc(iovec_count * sizeof(sg_iovec_t)); buff_len = xfer_len; segment_size = IPR_MAX_XFER; for (i = 0; (i < iovec_count) && (buff_len != 0); i++) { posix_memalign(&(iovec[i].iov_base), IPR_S_G_BUFF_ALIGNMENT, segment_size); if (data_direction == SG_DXFER_TO_DEV) memcpy(iovec[i].iov_base, data + (IPR_MAX_XFER * i), segment_size); iovec[i].iov_len = segment_size; buff_len -= segment_size; if (buff_len < segment_size) segment_size = buff_len; } iovec_count = i; dxferp = (void *)iovec; } else { iovec_count = 0; dxferp = data; } for (i = 0; i < (retries + 1); i++) { memset(&io_hdr_t, 0, sizeof(io_hdr_t)); memset(sense_data, 0, sizeof(struct sense_data_t)); io_hdr_t.interface_id = 'S'; io_hdr_t.cmd_len = cdb_size[(cdb[0] >> 5) & 0x7]; io_hdr_t.iovec_count = iovec_count; io_hdr_t.flags = 0; io_hdr_t.pack_id = 0; io_hdr_t.usr_ptr = 0; io_hdr_t.sbp = (unsigned char *)sense_data; io_hdr_t.mx_sb_len = sizeof(struct sense_data_t); io_hdr_t.timeout = timeout_in_sec * 1000; io_hdr_t.cmdp = cdb; io_hdr_t.dxfer_direction = data_direction; io_hdr_t.dxfer_len = xfer_len; io_hdr_t.dxferp = dxferp; rc = ioctl(fd, SG_IO, &io_hdr_t); if (rc == -1 && errno == EINVAL) { rc = -EINVAL; goto out; } if (rc == 0 && io_hdr_t.masked_status == CHECK_CONDITION) rc = CHECK_CONDITION; else if (rc == 0 && (io_hdr_t.host_status || io_hdr_t.driver_status)) rc = -EIO; if (rc == 0 || io_hdr_t.host_status == 1) break; } out: if (iovec_count) { for (i = 0, buf = (u8 *)data; i < iovec_count; i++) { if (data_direction == SG_DXFER_FROM_DEV) memcpy(buf, iovec[i].iov_base, iovec[i].iov_len); buf += iovec[i].iov_len; free(iovec[i].iov_base); } free(iovec); } return rc; }; /** * sg_ioctl - * @fd: file descriptor * @cdb: cdb * @data: data pointer * @xfer_len transfer length * @data_direction transfer to dev or from dev * @sense_data sense data pointer * @timeout_in_sec timeout value * * Returns: * 0 if success / non-zero on failure **/ int sg_ioctl(int fd, u8 cdb[IPR_CCB_CDB_LEN], void *data, u32 xfer_len, u32 data_direction, struct sense_data_t *sense_data, u32 timeout_in_sec) { return _sg_ioctl(fd, cdb, data, xfer_len, data_direction, sense_data, timeout_in_sec, 1); }; /** * sg_ioctl_noretry - * @fd: file descriptor * @cdb: cdb * @data: data pointer * @xfer_len transfer length * @data_direction transfer to dev or from dev * @sense_data sense data pointer * @timeout_in_sec timeout value * * Returns: * 0 if success / non-zero on failure **/ int sg_ioctl_noretry(int fd, u8 cdb[IPR_CCB_CDB_LEN], void *data, u32 xfer_len, u32 data_direction, struct sense_data_t *sense_data, u32 timeout_in_sec) { return _sg_ioctl(fd, cdb, data, xfer_len, data_direction, sense_data, timeout_in_sec, 0); }; /** * sg_ioctl_by_name - * @name: file name * @cdb: cdb * @data: data pointer * @xfer_len transfer length * @data_direction transfer to dev or from dev * @sense_data sense data pointer * @timeout_in_sec timeout value * * Returns: * 0 if success / non-zero on failure **/ static int sg_ioctl_by_name(char *name, u8 cdb[IPR_CCB_CDB_LEN], void *data, u32 xfer_len, u32 data_direction, struct sense_data_t *sense_data, u32 timeout_in_sec) { int fd, rc; if (strlen(name) <= 0) return -ENOENT; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s.\n", name); return errno; } rc = sg_ioctl(fd, cdb, data, xfer_len, data_direction, sense_data, timeout_in_sec); close(fd); return rc; } /** * ipr_set_ha_mode - * @ioa: ipr ioa struct * @gscsi_only_ha: process gscsi only flag * * Returns: * 0 if success / non-zero on failure **/ int ipr_set_ha_mode(struct ipr_ioa *ioa, int gscsi_only_ha) { int rc, len; struct sense_data_t sense_data; struct ipr_config_term_hdr *hdr; struct ipr_subsys_config_term *term; struct ipr_mode_pages pages; struct ipr_mode_page25 *page; memset(&sense_data, 0, sizeof(sense_data)); rc = mode_sense(&ioa->ioa, 0x25, &pages, &sense_data); if (!rc) { page = (struct ipr_mode_page25 *)(pages.data + pages.hdr.block_desc_len); for_each_page25_term(hdr, page) { if (hdr->term_id != IPR_SUBSYS_CONFIG_TERM_ID) continue; term = (struct ipr_subsys_config_term *)hdr; if (gscsi_only_ha) term->config = IPR_GSCSI_ONLY_HA_SUBSYS; else term->config = IPR_AFDASD_SUBSYS; len = pages.hdr.length + 1; pages.hdr.length = 0; return ipr_mode_select(&ioa->ioa, &pages, len); } } return rc; } /** * ipr_change_multi_adapter_assignment - set the preferred primary state and/or * the asymmetric access state * @ioa: ipr ioa struct * @preferred_primary: set preferred primary flag * * Returns: * 0 if success / non-zero on failure **/ int ipr_change_multi_adapter_assignment(struct ipr_ioa *ioa, int preferred_primary, int asym_access) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; struct ipr_dev *dev = &ioa->ioa; if (strlen(dev->gen_name) == 0) return -ENOENT; fd = open(dev->gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", dev->gen_name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_MAINTENANCE_OUT; cdb[1] = IPR_CHANGE_MULTI_ADAPTER_ASSIGNMENT; if (asym_access) cdb[2] = IPR_PRESERVE_ASYMMETRIC_STATE; if (preferred_primary) cdb[3] = IPR_IOA_STATE_PRIMARY; else cdb[3] = IPR_IOA_STATE_NO_PREFERENCE; rc = sg_ioctl(fd, cdb, NULL, 0, SG_DXFER_NONE, &sense_data, IPR_INTERNAL_DEV_TIMEOUT); if (rc != 0 && (sense_data.sense_key != ILLEGAL_REQUEST || ipr_debug)) scsi_cmd_err(dev, &sense_data, "Change Multi Adapter Assignment", rc); close(fd); return rc; } static void ipr_save_ioa_attr(struct ipr_ioa *ioa, char *field, char *value, int update); /** * set_preferred_primary - set the preferred primary state * @ioa: ipr ioa struct * @preferred_primary: set preferred primary flag * * Returns: * 0 if success / non-zero on failure **/ int set_preferred_primary(struct ipr_ioa *ioa, int preferred_primary) { char temp[100]; sprintf(temp, "%d", preferred_primary); if (ipr_change_multi_adapter_assignment(ioa, preferred_primary, IPR_PRESERVE_ASYMMETRIC_STATE)) return -EIO; return 0; } /** * set_ha_mode - * @ioa: ipr ioa struct * @gscsi_only: gscsi only flag * * Returns: * 0 if success / non-zero on failure **/ int set_ha_mode(struct ipr_ioa *ioa, int gscsi_only) { char temp[100]; int reset_needed = (gscsi_only != ioa->in_gscsi_only_ha); sprintf(temp, "%d", gscsi_only); if (ipr_set_ha_mode(ioa, gscsi_only)) return -EIO; ipr_save_ioa_attr(ioa, IPR_GSCSI_HA_ONLY, temp, 1); if (reset_needed) ipr_reset_adapter(ioa); return 0; } /** * ipr_set_active_active_mode - * @ioa: ipr ioa struct * @mode: enable or disable * * Returns: * 0 if success / non-zero on failure **/ int ipr_set_active_active_mode(struct ipr_ioa *ioa) { int len, rc; struct ipr_mode_pages mode_pages; struct ipr_mode_page24 *page24; memset(&mode_pages, 0, sizeof(mode_pages)); rc = ipr_mode_sense(&ioa->ioa, 0x24, &mode_pages); if (!rc) { page24 = (struct ipr_mode_page24 *) (((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); len = mode_pages.hdr.length +1; mode_pages.hdr.length = 0; page24->dual_adapter_af = ENABLE_DUAL_IOA_ACTIVE_ACTIVE; return ipr_mode_select(&ioa->ioa, &mode_pages, len); } return rc; } /** * set_active_active_mode - * @ioa: ipr ioa struct * @mode: enable or disable * * Returns: * 0 if success / non-zero on failure **/ int set_active_active_mode(struct ipr_ioa *ioa, int mode) { struct ipr_ioa_attr attr; int rc; /* Get the current ioa attributes. */ rc = ipr_get_ioa_attr(ioa, &attr); if (rc) return rc; /* Get the saved ioa attributes from the config file. */ rc = ipr_modify_ioa_attr(ioa, &attr); if (rc) return rc; if (attr.active_active == mode) { /* We should never get here. */ ioa_dbg(ioa, "Saved and current asymmetric access mode are not the same.\n"); } attr.active_active = mode; return ipr_set_ioa_attr(ioa, &attr, 1); } int ipr_set_ses_mode(struct ipr_dev *dev, int mode) { int rc; struct sense_data_t sense_data; u8 buff = (u8)mode; rc = __ipr_write_buffer(dev, 0x1f, &buff, sizeof(buff), &sense_data); return rc; } /** * get_scsi_dev_data - get scsi device data * @scsi_dev_ref: scsi_dev_data struct * * Returns: * number of devices if success / 0 or -1 on failure **/ int get_scsi_dev_data(struct scsi_dev_data **scsi_dev_ref) { int num_devs = 0; DIR *dirfd; struct dirent *dent; struct scsi_dev_data *scsi_dev_data; struct scsi_dev_data *scsi_dev_base = *scsi_dev_ref; dirfd = opendir("/sys/class/scsi_device"); if (!dirfd) { syslog_dbg("Failed to open scsi_device class. %m\n"); return 0; } while ((dent = readdir(dirfd)) != NULL) { int host, channel, lun; signed char id; char devpath[PATH_MAX]; char buff[256]; ssize_t len; if (sscanf(dent->d_name,"%d:%d:%hhd:%d", &host, &channel, &id, &lun) != 4) continue; scsi_dev_base = realloc(scsi_dev_base, sizeof(struct scsi_dev_data) * (num_devs + 1)); scsi_dev_data = &scsi_dev_base[num_devs]; scsi_dev_data->host = host; scsi_dev_data->channel = channel; scsi_dev_data->id = id; scsi_dev_data->lun = lun; strcpy(scsi_dev_data->sysfs_device_name, dent->d_name); sprintf(devpath, "/sys/class/scsi_device/%s/device", dent->d_name); len = sysfs_read_attr(devpath, "type", buff, 256); if (len > 0) sscanf(buff, "%d", &scsi_dev_data->type); //FIXME WHERE TO GET OPENS!!! /* sysfs_attr = sysfs_get_device_attr(sysfs_device_device, "opens"); sscanf(sysfs_attr->value "%d", &scsi_dev_data->opens); */ scsi_dev_data->opens = 0; len = sysfs_read_attr(devpath, "state", buff, 256); if (len > 0 && strstr(buff, "offline")) scsi_dev_data->online = 0; else scsi_dev_data->online = 1; len = sysfs_read_attr(devpath, "queue_depth", buff, 256); if (len > 0) sscanf(buff, "%d", &scsi_dev_data->qdepth); len = sysfs_read_attr(devpath, "adapter_handle", buff, 256); if (len > 0) sscanf(buff, "%X", &scsi_dev_data->handle); len = sysfs_read_attr(devpath, "vendor", buff, 256); if (len > 0) ipr_strncpy_0n(scsi_dev_data->vendor_id, buff, IPR_VENDOR_ID_LEN); len = sysfs_read_attr(devpath, "model", buff, 256); if (len > 0) ipr_strncpy_0n(scsi_dev_data->product_id, buff, IPR_PROD_ID_LEN); len = sysfs_read_attr(devpath, "resource_path", buff, 256); if (len > 0) ipr_strncpy_0n(scsi_dev_data->res_path, buff, IPR_MAX_RES_PATH_LEN); len = sysfs_read_attr(devpath, "device_id", buff, 256); if (len > 0) sscanf(buff, "%lX", &scsi_dev_data->device_id); strcpy(scsi_dev_data->dev_name,""); strcpy(scsi_dev_data->gen_name,""); num_devs++; } closedir(dirfd); *scsi_dev_ref = scsi_dev_base; return num_devs; } /** * wait_for_dev - wait 20 seconds for the device to become available * @name: device name * * Returns: * -ETIMEDOUT **/ static int wait_for_dev(char *name) { int fd, delay; for (delay = 20, fd = 0; delay; delay--) { fd = open(name, O_RDWR); if (fd != -1) { close(fd); break; } syslog_dbg("Waiting for %s to show up\n", name); sleep(1); } if (fd == -1) syslog_dbg("Failed to open %s.\n", name); return -ETIMEDOUT; } /** * get_sg_names - waits for sg devices to become available * @num_devs: number of devices * * Returns: * nothing **/ static void get_sg_names(int num_devs) { int i; DIR *dirfd; struct dirent *dent; char devpath[PATH_MAX]; for (i = 0; i < num_devs; i++) { sprintf(devpath, "/sys/class/scsi_device/%s/device/scsi_generic", scsi_dev_table[i].sysfs_device_name); dirfd = opendir(devpath); if (!dirfd) continue; while((dent = readdir(dirfd)) != NULL) { if (dent->d_name[0] == '.') continue; if (strncmp(dent->d_name, "sg", 2)) continue; sprintf(scsi_dev_table[i].gen_name, "/dev/%s", dent->d_name); break; } closedir(dirfd); } } /** * get_sd_names - populate the scsi_dev_table dev_name entries * @num_devs: number of devices * * Returns: * nothing **/ static void get_sd_names(int num_devs) { int i; DIR *dirfd; struct dirent *dent; char devpath[PATH_MAX]; for (i = 0; i < num_devs; i++) { sprintf(devpath, "/sys/class/scsi_device/%s/device/block", scsi_dev_table[i].sysfs_device_name); dirfd = opendir(devpath); if (!dirfd) continue; while((dent = readdir(dirfd)) != NULL) { if (dent->d_name[0] == '.') continue; if (strncmp(dent->d_name, "sd", 2)) continue; sprintf(scsi_dev_table[i].dev_name, "/dev/%s", dent->d_name); break; } closedir(dirfd); } } /** * get_ioa_name - populate adapter information in the scsi_dev_table * @ioa: ipr ioa struct * @num_sg_devices: number of sg devices * * Returns: * nothing **/ static void get_ioa_name(struct ipr_ioa *ioa, int num_sg_devices) { int i; int host_no; int channel = IPR_IOAFP_VIRTUAL_BUS; int id = 0; int lun = 0; sscanf(ioa->host_name, "host%d", &host_no); if (!ioa->sis64) channel = id = lun = 255; for (i = 0; i < num_sg_devices; i++) { if (scsi_dev_table[i].host == host_no && scsi_dev_table[i].channel == channel && scsi_dev_table[i].id == id && scsi_dev_table[i].lun == lun && scsi_dev_table[i].type == IPR_TYPE_ADAPTER) { strcpy(ioa->ioa.dev_name, scsi_dev_table[i].dev_name); strcpy(ioa->ioa.gen_name, scsi_dev_table[i].gen_name); ioa->ioa.scsi_dev_data = &scsi_dev_table[i]; ioa->ioa.ioa = ioa; } } } struct ipr_dual_ioa_state { u8 state; char *desc; }; static struct ipr_dual_ioa_state dual_ioa_states [] = { {IPR_IOA_STATE_PRIMARY, "Primary"}, {IPR_IOA_STATE_SECONDARY, "Secondary"}, {IPR_IOA_STATE_NO_PREFERENCE, "No Preference"} }; /** * print_ioa_state - copy the ioa state into buf * @buf: data buffer * @state: state * * Returns: * nothing **/ static void print_ioa_state(char *buf, u8 state) { int i; for (i = 0; i < ARRAY_SIZE(dual_ioa_states); i++) { if (dual_ioa_states[i].state == state) { strcpy(buf, dual_ioa_states[i].desc); return; } } sprintf(buf, "Unknown (%d)", state); } /** * get_subsys_config - * @ioa: ipr ioa struct * * Returns: * nothing **/ static void get_subsys_config(struct ipr_ioa *ioa) { int rc; struct ipr_mode_pages pages; struct ipr_mode_page25 *page; struct ipr_config_term_hdr *hdr; struct ipr_subsys_config_term *term; struct sense_data_t sense_data; ioa->in_gscsi_only_ha = 0; if (!ioa->gscsi_only_ha) return; memset(&sense_data, 0, sizeof(sense_data)); rc = mode_sense(&ioa->ioa, 0x25, &pages, &sense_data); if (!rc) { page = (struct ipr_mode_page25 *)(pages.data + pages.hdr.block_desc_len); for_each_page25_term(hdr, page) { if (hdr->term_id != IPR_SUBSYS_CONFIG_TERM_ID) continue; term = (struct ipr_subsys_config_term *)hdr; if (term->config == IPR_GSCSI_ONLY_HA_SUBSYS) ioa->in_gscsi_only_ha = 1; return; } } } /** * get_dual_ioa_state - set ioa->is_secondary to indicate the dual ioa state * @ioa: ipr ioa struct * * Returns: * nothing **/ static void get_dual_ioa_state(struct ipr_ioa *ioa) { int rc; struct ipr_dual_ioa_entry *ioa_entry; sprintf(ioa->dual_state, "Primary"); sprintf(ioa->preferred_dual_state, "No Preference"); ioa->is_secondary = 0; if (!ioa->dual_raid_support) return; rc = ipr_query_multi_ioa_status(ioa, &ioa->ioa_status, sizeof(ioa->ioa_status)); if (rc) return; print_ioa_state(ioa->preferred_dual_state, ioa->ioa_status.cap.preferred_role); if (ntohl(ioa->ioa_status.num_entries)) { ioa_entry = (struct ipr_dual_ioa_entry *) (((unsigned long)&ioa->ioa_status.cap) + ntohl(ioa->ioa_status.cap.length)); print_ioa_state(ioa->dual_state, ioa_entry->cur_state); if (ioa_entry->cur_state == IPR_IOA_STATE_SECONDARY) ioa->is_secondary = 1; } } /** * get_af_block_size - return the af block size * @ioa_cap: ipr_inquiry_ioa_cap struct * * Returns: * the actual af_block_size or IPR_DEFAULT_AF_BLOCK_SIZE **/ static u16 get_af_block_size(struct ipr_inquiry_ioa_cap *ioa_cap) { int sz_off = offsetof(struct ipr_inquiry_ioa_cap, af_block_size); int len_off = offsetof(struct ipr_inquiry_ioa_cap, page_length); if (ioa_cap->page_length > (sz_off - len_off)) return ntohs(ioa_cap->af_block_size); return IPR_DEFAULT_AF_BLOCK_SIZE; } /** * get_ioa_cap - get the capability information for the ioa (inquiry page D0) * Also, if page 01 is supported, then there is cache on the card. * @ioa: ipr ioa struct * * Returns: * nothing **/ static void get_ioa_cap(struct ipr_ioa *ioa) { int rc, j; struct ipr_inquiry_page0 page0_inq; struct ipr_inquiry_ioa_cap ioa_cap; struct ipr_mode_page24 *page24; struct ipr_mode_pages mode_pages; ioa->af_block_size = IPR_DEFAULT_AF_BLOCK_SIZE; ioa->tcq_mode = ioa_get_tcq_mode(ioa); rc = ipr_inquiry(&ioa->ioa, 0, &page0_inq, sizeof(page0_inq)); if (rc) { ioa->ioa_dead = 1; return; } for (j = 0; j < page0_inq.page_length; j++) { if (page0_inq.supported_page_codes[j] == 0x01) { ioa->has_cache = 1; continue; } if (page0_inq.supported_page_codes[j] == 0xC4) { ioa->has_cache = 1; continue; } if (page0_inq.supported_page_codes[j] != 0xD0) continue; rc = ipr_inquiry(&ioa->ioa, 0xD0, &ioa_cap, sizeof(ioa_cap)); if (rc) break; ioa->af_block_size = get_af_block_size(&ioa_cap); ioa->support_4k = ioa_cap.af_4k_support; if (ioa_cap.is_aux_cache) ioa->is_aux_cache = 1; if (ioa_cap.can_attach_to_aux_cache && ioa_cap.is_dual_wide) ioa->protect_last_bus = 1; if (ioa_cap.gscsi_only_ha) ioa->gscsi_only_ha = 1; if (ioa_cap.sis_format == IPR_SIS64) ioa->sis64 = 1; else { if (ioa_cap.ra_id_encoding == IPR_2BIT_HOP) ioa->hop_count = IPR_2BIT_HOP; else ioa->hop_count = IPR_3BIT_HOP; } if (ioa_cap.dual_ioa_raid || ioa_cap.dual_ioa_asymmetric_access) { memset(&mode_pages, 0, sizeof(mode_pages)); rc = ipr_mode_sense(&ioa->ioa, 0x24, &mode_pages); if (rc) break; page24 = (struct ipr_mode_page24 *) (((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); if (ioa_cap.dual_ioa_raid && page24->dual_adapter_af == ENABLE_DUAL_IOA_AF) ioa->dual_raid_support = 1; if (ioa_cap.dual_ioa_asymmetric_access) { ioa->asymmetric_access = 1; if (page24->dual_adapter_af == ENABLE_DUAL_IOA_ACTIVE_ACTIVE) { ioa->dual_raid_support = 1; ioa->asymmetric_access_enabled = 1; } else { ioa->asymmetric_access_enabled = 0; } } } } } /** * get_prot_levels - populate the prot_level_str field for each array, for each * vset and each device in the vset * @ioa: ipr ioa struct * * Returns: * nothing **/ static void get_prot_levels(struct ipr_ioa *ioa) { struct ipr_dev *array, *vset, *dev; char *prot_level_str; for_each_array(ioa, array) { prot_level_str = get_prot_level_str(ioa->supported_arrays, array->raid_level); strncpy(array->prot_level_str, prot_level_str, 8); } for_each_vset(ioa, vset) { prot_level_str = get_prot_level_str(ioa->supported_arrays, vset->raid_level); strncpy(vset->prot_level_str, prot_level_str, 8); for_each_dev_in_vset(vset, dev) strncpy(dev->prot_level_str, prot_level_str, 8); } } void ipr_convert_res_path_to_bytes(struct ipr_dev *dev) { struct scsi_dev_data *scsi_dev_data = dev->scsi_dev_data; int i; char *startptr, *endptr; if (scsi_dev_data) { startptr = dev->res_path_name; i = 0; do { dev->res_path[0].res_path_bytes[i++] = (u8)strtol(startptr, &endptr, 16); startptr = endptr + 1; } while (*endptr != '\0' && i < 8); while ( i < 8 ) dev->res_path[0].res_path_bytes[i++] = 0xff; } } /** * get_res_addr - * @dev: ipr dev struct * @res_addr: ipr_res_addr struct * * Returns: * 0 if success / -1 on failure **/ static int get_res_addr(struct ipr_dev *dev, struct ipr_res_addr *res_addr) { struct ipr_dev_record *dev_record = dev->dev_rcd; struct ipr_array_record *array_record = dev->array_rcd; if (dev->scsi_dev_data) { res_addr->host = dev->scsi_dev_data->host; res_addr->bus = dev->scsi_dev_data->channel; res_addr->target = dev->scsi_dev_data->id; res_addr->lun = dev->scsi_dev_data->lun; if (dev->ioa->sis64) { strncpy(dev->res_path_name, dev->scsi_dev_data->res_path, strlen(dev->scsi_dev_data->res_path)); ipr_convert_res_path_to_bytes(dev); } } else if (ipr_is_af_dasd_device(dev)) { if (dev_record && dev_record->no_cfgte_dev) { res_addr->host = dev->ioa->host_num; res_addr->bus = dev_record->type2.last_resource_addr.bus; res_addr->target = dev_record->type2.last_resource_addr.target; res_addr->lun = dev_record->type2.last_resource_addr.lun; } else if (dev_record) { res_addr->host = dev->ioa->host_num; res_addr->bus = dev_record->type2.resource_addr.bus; res_addr->target = dev_record->type2.resource_addr.target; res_addr->lun = dev_record->type2.resource_addr.lun; if (dev->ioa->sis64) { ipr_format_res_path(dev_record->type3.res_path, dev->res_path_name, IPR_MAX_RES_PATH_LEN); ipr_convert_res_path_to_bytes(dev); } } else return -1; } else if (ipr_is_volume_set(dev)) { if (array_record && array_record->no_config_entry) { res_addr->host = dev->ioa->host_num; res_addr->bus = array_record->type2.last_resource_addr.bus; res_addr->target = array_record->type2.last_resource_addr.target; res_addr->lun = array_record->type2.last_resource_addr.lun; } else if (array_record) { res_addr->host = dev->ioa->host_num; res_addr->bus = array_record->type2.resource_addr.bus; res_addr->target = array_record->type2.resource_addr.target; res_addr->lun = array_record->type2.resource_addr.lun; } else return -1; } else return -1; return 0; } /** * find_multipath_vset - return the other vset of a given multipath vset * @vset1: ipr dev struct * * Returns: * ipr_dev if success / NULL on failure **/ static struct ipr_dev *find_multipath_vset(struct ipr_dev *vset1) { struct ipr_ioa *ioa; struct ipr_dev *vset2; for_each_ioa(ioa) { if (ioa == vset1->ioa) continue; for_each_vset(ioa, vset2) { if (memcmp(vset1->vendor_id, vset2->vendor_id, IPR_VENDOR_ID_LEN)) continue; if (memcmp(vset1->product_id, vset2->product_id, IPR_PROD_ID_LEN)) continue; if (memcmp(vset1->serial_number, vset2->serial_number, IPR_SERIAL_NUM_LEN)) continue; return vset2; } } return NULL; } /** * link_multipath_vsets - set the alt_path entries for multipath vsets * * Returns: * nothing **/ static void link_multipath_vsets() { struct ipr_ioa *ioa; struct ipr_dev *vset1, *vset2; for_each_ioa(ioa) { for_each_vset(ioa, vset1) { vset2 = find_multipath_vset(vset1); if (!vset2) continue; vset1->alt_path = vset2; vset2->alt_path = vset1; } } } /** * ipr_format_res_path - Format the resource path into a string. * @res_path: resource path * @buf: buffer * @len: buffer length * * Return value: * none **/ void ipr_format_res_path(u8 *res_path, char *buffer, int len) { int i; char *p = buffer; *p = '\0'; p += snprintf(p, buffer + len - p, "%02X", res_path[0]); for (i = 1; res_path[i] != 0xff && ((i * 3) < len); i++) p += snprintf(p, buffer + len - p, "-%02X", res_path[i]); } /** * ipr_format_res_path_wo_hyphen - Format the resource path into a string. * @res_path: resource path * @buf: buffer * @len: buffer length * * Return value: * none **/ void ipr_format_res_path_wo_hyphen(u8 *res_path, char *buffer, int len) { int i; char *p = buffer; *p = '\0'; p += snprintf(p, buffer + len - p, "%02X", res_path[0]); for (i = 1; res_path[i] != 0xff && ((i * 3) < len); i++) p += snprintf(p, buffer + len - p, "%02X", res_path[i]); } /** * ipr_res_path_cmp - compare two resource paths * @dev_res_path * @scsi_res_path * * Returns: * 1 if the paths are the same, 0 otherwise **/ int ipr_res_path_cmp(u8 *dev_res_path, char *scsi_res_path) { char buffer[IPR_MAX_RES_PATH_LEN]; ipr_format_res_path(dev_res_path, buffer, IPR_MAX_RES_PATH_LEN); return !strncmp(buffer, scsi_res_path, IPR_MAX_RES_PATH_LEN); } /** * ipr_debug_dump_rcd - dump out a device record * @rcd: record structure * * Returns: * nothing */ void ipr_debug_dump_rcd(struct ipr_common_record *rcd) { int i; u8 *rcd_ptr = (u8 *)rcd; dprintf("===========\n"); dprintf("Record ID = %d, Length = %d\n", rcd->record_id, rcd->record_len); for (i=0; irecord_len; i++) { dprintf("%02x", rcd_ptr[i]); if ((i+1)%8 == 0) dprintf(" "); if ((i+1)%32 == 0) dprintf("\n"); } dprintf("\n"); } /** * ipr_get_logical_block_size - check the logical block size * @dev: ipr dev struct * * Return value: * none **/ int ipr_get_logical_block_size(struct ipr_dev *dev) { char path[PATH_MAX], *first_hyphen; char buff[16]; ssize_t len; int rc; first_hyphen = strchr(dev->dev_name, 's'); sprintf(path, "/sys/block/%s/queue", first_hyphen); len = sysfs_read_attr(path, "logical_block_size", buff, 16); if (len < 0) { syslog_dbg("Failed to open logical_block_size parameter.\n"); return -1; } rc = atoi(buff); return rc; } /** * check_current_config - populates the ioa configuration data * @allow_rebuild_refresh: allow_rebuild_refresh flag * * Returns: * nothing **/ void check_current_config(bool allow_rebuild_refresh) { struct scsi_dev_data *scsi_dev_data; int num_sg_devices, rc, device_count, j, k; struct ipr_ioa *ioa; struct ipr_array_query_data *qac_data; struct ipr_common_record *common_record; struct ipr_dev_record *device_record; struct ipr_array_record *array_record; struct ipr_std_inq_data std_inq_data; struct sense_data_t sense_data; struct ipr_res_addr res_addr, *ra; struct ipr_dev *dev; int *qac_entry_ref; struct ipr_dev_identify_vpd di_vpd; char *pchr; if (ipr_qac_data == NULL) { ipr_qac_data = (struct ipr_array_query_data *) calloc(num_ioas, sizeof(struct ipr_array_query_data)); } /* Get sg data via sysfs */ num_sg_devices = get_scsi_dev_data(&scsi_dev_table); get_sg_names(num_sg_devices); get_sd_names(num_sg_devices); for(ioa = ipr_ioa_head, qac_data = ipr_qac_data; ioa; ioa = ioa->next, qac_data++) { get_ioa_name(ioa, num_sg_devices); ioa->num_devices = 0; rc = ipr_inquiry(&ioa->ioa, IPR_STD_INQUIRY, &std_inq_data, sizeof(std_inq_data)); if (rc) ioa->ioa_dead = 1; get_ioa_cap(ioa); get_dual_ioa_state(ioa); get_subsys_config(ioa); /* Get Query Array Config Data */ rc = ipr_query_array_config(ioa, allow_rebuild_refresh, 0, 0, 0, qac_data); if (rc != 0) { qac_data->num_records = 0; qac_data->resp_len = qac_data->hdr_len; } ioa->qac_data = qac_data; ioa->start_array_qac_entry = NULL; device_count = 0; memset(ioa->dev, 0, IPR_MAX_IOA_DEVICES * sizeof(struct ipr_dev)); qac_entry_ref = calloc(1, sizeof(int) * qac_data->num_records); /* now assemble data pertaining to each individual device */ for (j = 0, scsi_dev_data = scsi_dev_table; j < num_sg_devices; j++, scsi_dev_data++) { if (scsi_dev_data->host != ioa->host_num) continue; if (scsi_dev_data->type == TYPE_DISK || scsi_dev_data->type == IPR_TYPE_AF_DISK || scsi_dev_data->type == IPR_TYPE_ADAPTER || scsi_dev_data->type == TYPE_ENCLOSURE || scsi_dev_data->type == TYPE_ROM || scsi_dev_data->type == TYPE_TAPE || scsi_dev_data->type == TYPE_PROCESSOR) { ioa->dev[device_count].ioa = ioa; ioa->dev[device_count].scsi_dev_data = scsi_dev_data; ioa->dev[device_count].qac_entry = NULL; strcpy(ioa->dev[device_count].dev_name, scsi_dev_data->dev_name); strcpy(ioa->dev[device_count].gen_name, scsi_dev_data->gen_name); if (ioa->support_4k && scsi_dev_data->type == TYPE_DISK) { if (ipr_get_logical_block_size(&ioa->dev[device_count]) == IPR_JBOD_4K_BLOCK_SIZE) ioa->dev[device_count].block_dev_class |= IPR_BLK_DEV_CLASS_4K; } /* find array config data matching resource entry */ k = 0; for_each_qac_entry(common_record, qac_data) { if (common_record->record_id == IPR_RECORD_ID_DEVICE_RECORD) { device_record = (struct ipr_dev_record *)common_record; if (device_record->type2.resource_addr.bus == scsi_dev_data->channel && device_record->type2.resource_addr.target == scsi_dev_data->id && device_record->type2.resource_addr.lun == scsi_dev_data->lun) { ioa->dev[device_count].qac_entry = common_record; qac_entry_ref[k]++; break; } } else if (common_record->record_id == IPR_RECORD_ID_ARRAY_RECORD) { array_record = (struct ipr_array_record *)common_record; if (array_record->type2.resource_addr.bus == scsi_dev_data->channel && array_record->type2.resource_addr.target == scsi_dev_data->id && array_record->type2.resource_addr.lun == scsi_dev_data->lun) { ioa->dev[device_count].qac_entry = common_record; qac_entry_ref[k]++; break; } } else if (common_record->record_id == IPR_RECORD_ID_DEVICE_RECORD_3) { device_record = (struct ipr_dev_record *)common_record; if (ipr_res_path_cmp(device_record->type3.res_path, scsi_dev_data->res_path)) { ioa->dev[device_count].qac_entry = common_record; qac_entry_ref[k]++; break; } } else if (common_record->record_id == IPR_RECORD_ID_VSET_RECORD_3) { array_record = (struct ipr_array_record *)common_record; if (ipr_res_path_cmp(array_record->type3.res_path, scsi_dev_data->res_path)) { ioa->dev[device_count].qac_entry = common_record; qac_entry_ref[k]++; break; } } else if (common_record->record_id == IPR_RECORD_ID_ARRAY_RECORD_3) { array_record = (struct ipr_array_record *)common_record; if (ipr_res_path_cmp(array_record->type3.res_path, scsi_dev_data->res_path)) { ioa->dev[device_count].qac_entry = common_record; qac_entry_ref[k]++; break; } } k++; } /* Send Test Unit Ready to start device if its a volume set */ /* xxx TODO try to remove this */ if (!ipr_fast && ipr_is_volume_set(&ioa->dev[device_count])) __ipr_test_unit_ready(&ioa->dev[device_count], &sense_data); device_count++; } else if (scsi_dev_data->type == IPR_TYPE_ADAPTER) { ioa->ioa.ioa = ioa; ioa->ioa.scsi_dev_data = scsi_dev_data; } } /* now scan qac device and array entries to see which ones have not been referenced */ k = 0; for_each_qac_entry(common_record, qac_data) { if (qac_entry_ref[k] > 1) syslog(LOG_ERR, "Query Array Config entry referenced more than once\n"); if (common_record->record_id == IPR_RECORD_ID_SUPPORTED_ARRAYS) { ioa->supported_arrays = (struct ipr_supported_arrays *)common_record; } else if (!qac_entry_ref[k] && (ipr_is_device_record(common_record->record_id) || ipr_is_vset_record(common_record->record_id))) { // TODO - type 3 array records???? array_record = (struct ipr_array_record *)common_record; if (ipr_is_vset_record(common_record->record_id) && array_record->start_cand) { ioa->start_array_qac_entry = array_record; } else { /* add phantom qac entry to ioa device list */ ioa->dev[device_count].scsi_dev_data = NULL; ioa->dev[device_count].qac_entry = common_record; ioa->dev[device_count].ioa = ioa; strcpy(ioa->dev[device_count].dev_name, ""); strcpy(ioa->dev[device_count].gen_name, ""); device_count++; } } k++; } ioa->num_devices = device_count; free(qac_entry_ref); } for_each_ioa(ioa) { for_each_dev(ioa, dev) { get_res_addr(dev, &res_addr); for_each_ra(ra, dev) memcpy(ra, &res_addr, sizeof(*ra)); if (!dev || !dev->qac_entry) continue; if (dev->qac_entry->record_id == IPR_RECORD_ID_DEVICE_RECORD) { dev->vendor_id = dev->dev_rcd->type2.vendor_id; dev->product_id = dev->dev_rcd->type2.product_id; dev->serial_number = dev->dev_rcd->type2.serial_number; dev->array_id = dev->dev_rcd->type2.array_id; dev->resource_handle = dev->dev_rcd->type2.resource_handle; dev->block_dev_class = dev->dev_rcd->type2.block_dev_class; } else if (dev->qac_entry->record_id == IPR_RECORD_ID_DEVICE_RECORD_3) { dev->vendor_id = dev->dev_rcd->type3.vendor_id; dev->product_id = dev->dev_rcd->type3.product_id; dev->serial_number = dev->dev_rcd->type3.serial_number; dev->array_id = dev->dev_rcd->type3.array_id; dev->resource_handle = dev->dev_rcd->type3.resource_handle; dev->block_dev_class = dev->dev_rcd->type3.block_dev_class; } else if (dev->qac_entry->record_id == IPR_RECORD_ID_ARRAY_RECORD) { dev->vendor_id = dev->array_rcd->type2.vendor_id; dev->product_id = dev->array_rcd->type2.product_id; dev->serial_number = dev->array_rcd->type2.serial_number; dev->array_id = dev->array_rcd->type2.array_id; dev->raid_level = dev->array_rcd->type2.raid_level; dev->stripe_size = dev->array_rcd->type2.stripe_size; dev->resource_handle = dev->array_rcd->type2.resource_handle; dev->block_dev_class = dev->array_rcd->type2.block_dev_class; } else if (dev->qac_entry->record_id == IPR_RECORD_ID_VSET_RECORD_3) { dev->vendor_id = dev->array_rcd->type3.vendor_id; dev->product_id = dev->array_rcd->type3.product_id; dev->serial_number = dev->array_rcd->type3.serial_number; dev->array_id = dev->array_rcd->type3.array_id; dev->raid_level = dev->array_rcd->type3.raid_level; dev->stripe_size = dev->array_rcd->type3.stripe_size; dev->resource_handle = dev->array_rcd->type3.resource_handle; dev->block_dev_class = dev->array_rcd->type3.block_dev_class; } else if (dev->qac_entry->record_id == IPR_RECORD_ID_ARRAY_RECORD_3) { dev->vendor_id = dev->array_rcd->type3.vendor_id; dev->product_id = dev->array_rcd->type3.product_id; dev->serial_number = dev->array_rcd->type3.serial_number; dev->array_id = dev->array_rcd->type3.array_id; dev->raid_level = dev->array_rcd->type3.raid_level; dev->stripe_size = dev->array_rcd->type3.stripe_size; dev->resource_handle = dev->array_rcd->type3.resource_handle; dev->block_dev_class = dev->array_rcd->type3.block_dev_class; } } get_prot_levels(ioa); } for_each_ioa(ioa) { if (strlen((char *)ioa->yl_serial_num) == 0) { memset(&di_vpd, 0, sizeof(di_vpd)); rc = ipr_inquiry(&ioa->ioa, 0x83, &di_vpd, sizeof(di_vpd)); if (!rc && ntohs(di_vpd.add_page_len) > 120) { pchr = strstr((char *)&di_vpd.dev_identify_contxt[0],"SN"); if (pchr) strncpy((char *)ioa->yl_serial_num, (pchr + 3), YL_SERIAL_NUM_LEN); } } } set_devs_format_completed(); link_multipath_vsets(); ipr_cleanup_zeroed_devs(); resolve_old_config(); } /** * num_devices_opens - return usage count (number of opens) for a given device * @host_num: host number * @channel: channel number * @id: id number * @lun: lun number * * Returns: * usage count (number of opens) for a given device **/ /* xxx TODO delete */ int num_device_opens(int host_num, int channel, int id, int lun) { struct scsi_dev_data *scsi_dev_base = NULL; int opens = 0; int k, num_sg_devices; /* Get sg data via sg proc file system */ num_sg_devices = get_scsi_dev_data(&scsi_dev_base); /* find usage counts in scsi_dev_data */ for (k = 0; k < num_sg_devices; k++) { if ((host_num == scsi_dev_base[k].host) && (channel == scsi_dev_base[k].channel) && (id == scsi_dev_base[k].id) && (lun == scsi_dev_base[k].lun)) { opens = scsi_dev_base[k].opens; break; } } free(scsi_dev_base); return opens; } /** * open_and_lock - Open and device file and lock it. * @file_name: the name of the device to open * * Returns: * file descriptor if success / -1 on failure **/ int open_and_lock(char *file_name) { int fd; int rc; fd = open(file_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", file_name); return fd; } rc = flock(fd, LOCK_EX); if (rc) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not lock %s. %m\n", file_name); close(fd); return rc; } return fd; } /** * exit_on_error - exits program or error after cleaning up * @s: string * @...: arguments * * Returns: * nothing - exits program on error **/ void exit_on_error(char *s, ...) { va_list args; char usr_str[256]; exit_func(); va_start(args, s); vsprintf(usr_str, s, args); va_end(args); closelog(); openlog(tool_name, LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); syslog(LOG_ERR,"%s",usr_str); closelog(); exit(1); } /** * ipr_config_file_hdr - prints the header to the ipr config file * @file_name: file name * * Returns: * nothing **/ static void ipr_config_file_hdr(char *file_name) { FILE *fd; char cmd_str[64]; if (strlen(file_name) == 0) return; /* check if file currently exists */ fd = fopen(file_name, "r"); if (fd) { fclose(fd); return; } /* be sure directory is present */ sprintf(cmd_str,"install -d %s",IPR_CONFIG_DIR); system(cmd_str); /* otherwise, create new file */ fd = fopen(file_name, "w"); if (fd == NULL) { syslog(LOG_ERR, "Could not open %s. %m\n", file_name); return; } fprintf(fd,"# DO NOT EDIT! Software generated configuration file for\n"); fprintf(fd,"# ipr SCSI device subsystem\n\n"); fprintf(fd,"# Use iprconfig to configure\n"); fprintf(fd,"# See \"man iprconfig\" for more information\n\n"); fclose(fd); } /** * ipr_save_attr - * @ioa: ipr ioa struct * @category: * @field: * @value: * @update: * * Returns: * nothing **/ static void ipr_save_attr(struct ipr_ioa *ioa, char *category, char *field, char *value, int update) { char fname[64]; FILE *fd, *temp_fd; char temp_fname[64], line[64]; int bus_found = 0; int field_found = 0; sprintf(fname, "%s%x_%s", IPR_CONFIG_DIR, ioa->ccin, ioa->pci_address); ipr_config_file_hdr(fname); fd = fopen(fname, "r"); if (fd == NULL) return; sprintf(temp_fname, "%s.1", fname); temp_fd = fopen(temp_fname, "w"); if (temp_fd == NULL) { syslog(LOG_ERR, "Could not open %s. %m\n", temp_fname); return; } while (fgets(line, 64, fd)) { if (strstr(line, category) && line[0] != '#') { bus_found = 1; } else { if (bus_found) { if (line[0] == '[') { bus_found = 0; if (!field_found) { if (!update) fprintf(temp_fd, "# "); fprintf(temp_fd,"%s %s\n",field,value); } } if (strstr(line, field)) { if (update) sprintf(line,"%s %s\n",field,value); field_found = 1; } } } fputs(line, temp_fd); } if (!field_found) { if (!bus_found) fprintf(temp_fd,"\n%s\n", category); if (!update) fprintf(temp_fd, "# "); fprintf(temp_fd,"%s %s\n", field, value); } if (rename(temp_fname, fname)) syslog(LOG_ERR, "Could not save %s.\n", fname); fclose(fd); fclose(temp_fd); } /** * ipr_save_bus_attr - * @ioa: ipr ioa struct * @bus: bus * @field: * @value: * @update: * * Returns: * nothing **/ static void ipr_save_bus_attr(struct ipr_ioa *ioa, int bus, char *field, char *value, int update) { char category[16]; sprintf(category,"[%s %d]", IPR_CATEGORY_BUS, bus); ipr_save_attr(ioa, category, field, value, update); } /** * ipr_save_dev_attr - * @dev: ipr dev struct * @field: * @value: * @update: * * Returns: * nothing **/ static void ipr_save_dev_attr(struct ipr_dev *dev, char *field, char *value, int update) { char category[16]; sprintf(category,"[%s %d:%d:%d]", IPR_CATEGORY_DISK, dev->scsi_dev_data->channel, dev->scsi_dev_data->id, dev->scsi_dev_data->lun); ipr_save_attr(dev->ioa, category, field, value, update); } /** * ipr_save_ioa_attr - * @ioa: ipr ioa struct * @field: * @value: * @update: * * Returns: * nothing **/ static void ipr_save_ioa_attr(struct ipr_ioa *ioa, char *field, char *value, int update) { char category[16]; sprintf(category,"[%s]", IPR_CATEGORY_IOA); ipr_save_attr(ioa, category, field, value, update); } /** * ipr_get_saved_attr - * @ioa: ipr ioa struct * @category: * @field: * @value: * * Returns: * 0 if success / RC_FAILED on failure **/ static int ipr_get_saved_attr(struct ipr_ioa *ioa, char *category, char *field, char *value) { FILE *fd; char fname[64], line[64], *str_ptr; int bus_found = 0; sprintf(fname, "%s%x_%s", IPR_CONFIG_DIR, ioa->ccin, ioa->pci_address); fd = fopen(fname, "r"); if (fd == NULL) return RC_FAILED; while (NULL != fgets(line, 64, fd)) { if (line[0] != '#') { if (strstr(line, category)) bus_found = 1; else if (bus_found) { if (line[0] == '[') bus_found = 0; if ((str_ptr = strstr(line, field))) { str_ptr += strlen(field); while (str_ptr[0] == ' ') str_ptr++; sscanf(str_ptr, "%s\n", value); fclose(fd); return RC_SUCCESS; } } } } fclose(fd); return RC_FAILED; } /** * ipr_get_saved_bus_attr - * @ioa: ipr ioa struct * @bus: * @field: * @value: * * Returns: * 0 if success / RC_FAILED on failure **/ static int ipr_get_saved_bus_attr(struct ipr_ioa *ioa, int bus, char *field, char *value) { char category[16]; sprintf(category,"[%s %d]", IPR_CATEGORY_BUS, bus); return ipr_get_saved_attr(ioa, category, field, value); } /** * ipr_get_saved_dev_attr - * @dev: ipr dev struct * @field: * @value: * * Returns: * 0 if success / RC_FAILED on failure **/ static int ipr_get_saved_dev_attr(struct ipr_dev *dev, char *field, char *value) { char category[30]; if (!dev->scsi_dev_data) return -ENXIO; sprintf(category,"[%s %d:%d:%d]", IPR_CATEGORY_DISK, dev->scsi_dev_data->channel, dev->scsi_dev_data->id, dev->scsi_dev_data->lun); return ipr_get_saved_attr(dev->ioa, category, field, value); } /** * ipr_get_saved_ioa_attr - * @ioa: ipr ioa struct * @field: * @value: * * Returns: * 0 if success / RC_FAILED on failure **/ static int ipr_get_saved_ioa_attr(struct ipr_ioa *ioa, char *field, char *value) { char category[16]; sprintf(category,"[%s]", IPR_CATEGORY_IOA); return ipr_get_saved_attr(ioa, category, field, value); } #define GSCSI_TCQ_DEPTH 3 #define GSCSI_SAS_TCQ_DEPTH 16 #define AS400_TCQ_DEPTH 16 #define DEFAULT_TCQ_DEPTH 64 /** * get_tcq_depth - return the proper queue depth for the given device * @dev: ipr dev struct * * Returns: * GSCSI_TCQ_DEPTH, GSCSI_SAS_TCQ_DEPTH, AS400_TCQ_DEPTH or DEFAULT_TCQ_DEPTH **/ static int get_tcq_depth(struct ipr_dev *dev) { if (ipr_is_gscsi(dev)) { if (ioa_is_spi(dev->ioa)) return GSCSI_TCQ_DEPTH; else return GSCSI_SAS_TCQ_DEPTH; } if (!dev->scsi_dev_data) return AS400_TCQ_DEPTH; if (!strncmp(dev->scsi_dev_data->vendor_id, "IBMAS400", 8)) return AS400_TCQ_DEPTH; return DEFAULT_TCQ_DEPTH; } /** * is_tagged - * @dev: ipr dev struct * * Returns: * **/ static int is_tagged(struct ipr_dev *dev) { char temp[100]; if (!ipr_read_dev_attr(dev, "tcq_enable", temp, 100)) return strtoul(temp, NULL, 10); else if (!ipr_read_dev_attr(dev, "queue_type", temp, 100)) return (strstr(temp, "none") ? 0 : 1); return 0; } /** * set_tagged - * @dev: ipr dev struct * @tcq_enabled: * * Returns: * 0 if success / non-zero on failure **/ static int set_tagged(struct ipr_dev *dev, int tcq_enabled) { char temp[100]; if (!ipr_read_dev_attr(dev, "tcq_enable", temp, 100)) { sprintf(temp, "%d", tcq_enabled); return ipr_write_dev_attr(dev, "tcq_enable", temp); } if (!ipr_read_dev_attr(dev, "queue_type", temp, 100)) { if (!tcq_enabled) return ipr_write_dev_attr(dev, "queue_type", "none"); if (page0x0a_setup(dev)) return ipr_write_dev_attr(dev, "queue_type", "ordered"); else return ipr_write_dev_attr(dev, "queue_type", "simple"); } return -EIO; } /** * get_format_timeout - * @dev: ipr dev struct * * Returns: * timeout value **/ static int get_format_timeout(struct ipr_dev *dev) { struct ipr_query_dasd_timeouts tos; int rc, i, records, timeout; char temp[100]; if (ipr_is_af_dasd_device(dev)) { rc = ipr_query_dasd_timeouts(dev, &tos); if (!rc) { records = (ntohl(tos.length) - sizeof(tos.length)) / sizeof(tos.record[0]); for (i = 0; i < records; i++) { if (tos.record[i].op_code != FORMAT_UNIT) continue; if (IPR_TIMEOUT_RADIX_IS_MINUTE(tos.record[i].timeout)) return ((tos.record[i].timeout & IPR_TIMEOUT_MASK) * 60); if (IPR_TIMEOUT_RADIX_IS_SECONDS(tos.record[i].timeout)) return tos.record[i].timeout & IPR_TIMEOUT_MASK; scsi_dbg(dev, "Unknown timeout radix: %X\n", (tos.record[i].timeout & IPR_TIMEOUT_RADIX_MASK)); break; } } } timeout = IPR_FORMAT_UNIT_TIMEOUT; rc = ipr_get_saved_dev_attr(dev, IPR_FORMAT_TIMEOUT, temp); if (rc == RC_SUCCESS) sscanf(temp, "%d", &timeout); return timeout; } static const struct ipr_dasd_timeout_record ipr_dasd_timeouts[] = { {READ_10, 0, __constant_cpu_to_be16(30)}, {WRITE_10, 0, __constant_cpu_to_be16(30)}, {WRITE_VERIFY, 0, __constant_cpu_to_be16(30)}, {SKIP_READ, 0, __constant_cpu_to_be16(30)}, {SKIP_WRITE, 0, __constant_cpu_to_be16(30)} }; struct ipr_dasd_timeouts { u32 length; struct ipr_dasd_timeout_record record[ARRAY_SIZE(ipr_dasd_timeouts) + 1]; }; /** * ipr_set_dasd_timeouts - * @dev: ipr dev struct * @format_timeout: * * Returns: * 0 if success / non-zero on failure **/ static int ipr_set_dasd_timeouts(struct ipr_dev *dev, int format_timeout) { int fd, rc, len; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; struct ipr_dasd_timeouts timeouts; struct ipr_disk_attr attr; char *name = dev->gen_name; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memcpy(timeouts.record, ipr_dasd_timeouts, sizeof(ipr_dasd_timeouts)); len = sizeof(timeouts) - sizeof(timeouts.record[0]); if (!ipr_get_dev_attr(dev, &attr)) { len = sizeof(timeouts); if (!format_timeout) format_timeout = attr.format_timeout; timeouts.record[ARRAY_SIZE(ipr_dasd_timeouts)].op_code = FORMAT_UNIT; if (format_timeout >= IPR_TIMEOUT_MASK) { timeouts.record[ARRAY_SIZE(ipr_dasd_timeouts)].timeout = (format_timeout / 60) | IPR_TIMEOUT_MINUTE_RADIX; } else { timeouts.record[ARRAY_SIZE(ipr_dasd_timeouts)].timeout = format_timeout; } } timeouts.length = htonl(len); memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = SET_DASD_TIMEOUTS; cdb[7] = (len >> 8) & 0xff; cdb[8] = len & 0xff; rc = sg_ioctl(fd, cdb, &timeouts, len, SG_DXFER_TO_DEV, &sense_data, SET_DASD_TIMEOUTS_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Set DASD timeouts", rc); close(fd); return rc; } /** * ipr_get_dev_attr - * @ioa: ipr ioa struct * @attr: ipr_disk_attr struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_get_dev_attr(struct ipr_dev *dev, struct ipr_disk_attr *attr) { char temp[100]; struct ipr_mode_pages mode_pages; struct ipr_ioa_mode_page *page; if (ipr_read_dev_attr(dev, "queue_depth", temp, 100)) return -EIO; if (ipr_is_af_dasd_device(dev)) { memset(&mode_pages, 0, sizeof(mode_pages)); if (ipr_mode_sense(dev, 0x20, &mode_pages)) return -EIO; page = (struct ipr_ioa_mode_page *) (((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); attr->queue_depth = page->max_tcq_depth; } else attr->queue_depth = strtoul(temp, NULL, 10); if (ipr_is_af_dasd_device(dev)) { if (attr->queue_depth < 2) attr->tcq_enabled = 0; else attr->tcq_enabled = 1; } else attr->tcq_enabled = is_tagged(dev); attr->format_timeout = get_format_timeout(dev); return 0; } int ipr_set_format_completed_bit(struct ipr_dev *dev) { int len; struct ipr_mode_pages mode_pages; struct ipr_ioa_mode_page *page; memset(&mode_pages, 0, sizeof(mode_pages)); if (ipr_mode_sense(dev, 0x20, &mode_pages)) return -EIO; page = (struct ipr_ioa_mode_page *) (((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); page->format_completed = 1; len = mode_pages.hdr.length + 1; mode_pages.hdr.length = 0; mode_pages.hdr.medium_type = 0; mode_pages.hdr.device_spec_parms = 0; page->hdr.parms_saveable = 0; if (ipr_mode_select(dev, &mode_pages, len)) return -EIO; return 0; } /** * get_ioa_caching - * @ioa: ipr ioa struct * * Returns: * 0 **/ int get_ioa_caching(struct ipr_ioa *ioa) { int rc; int found = 0; struct ipr_query_ioa_caching_info info; struct ipr_global_cache_params_term *term; int term_size = sizeof(struct ipr_query_ioa_caching_info); memset(&info, 0, term_size); rc = ipr_query_cache_parameters(ioa, &info, term_size); if (rc) return IPR_IOA_REQUESTED_CACHING_DEFAULT; for_each_cache_term(term, &info) if (term && term->term_id == IPR_CACHE_PARAM_TERM_ID) { found = 1; break; } if (found == 1) if (term->enable_caching_dual_ioa_failure == IPR_IOA_CACHING_DUAL_FAILURE_ENABLED) if (term->disable_caching_requested == IPR_IOA_REQUESTED_CACHING_DISABLED) return IPR_IOA_CACHING_DISABLED_DUAL_ENABLED; else return IPR_IOA_CACHING_DEFAULT_DUAL_ENABLED; else if (term->disable_caching_requested == IPR_IOA_REQUESTED_CACHING_DISABLED) return IPR_IOA_REQUESTED_CACHING_DISABLED; else return IPR_IOA_REQUESTED_CACHING_DEFAULT; else return IPR_IOA_REQUESTED_CACHING_DEFAULT; } /** * ipr_get_ioa_attr - * @ioa: ipr ioa struct * @attr: ipr_ioa_attr struct * * Returns: * 0 **/ int ipr_get_ioa_attr(struct ipr_ioa *ioa, struct ipr_ioa_attr *attr) { attr->preferred_primary = 0; attr->gscsi_only_ha = ioa->in_gscsi_only_ha; attr->active_active = ioa->asymmetric_access_enabled; attr->caching = get_ioa_caching(ioa); if (!ioa->dual_raid_support) return 0; if (ioa->ioa_status.cap.preferred_role == IPR_IOA_STATE_PRIMARY) attr->preferred_primary = 1; return 0; } /** * ipr_modify_dev_attr - * @dev: ipr dev struct * @attr: ipr_disk_attr struct * * Returns: * 0 **/ int ipr_modify_dev_attr(struct ipr_dev *dev, struct ipr_disk_attr *attr) { char temp[100]; int rc; rc = ipr_get_saved_dev_attr(dev, IPR_QUEUE_DEPTH, temp); if (rc == RC_SUCCESS) sscanf(temp, "%d", &attr->queue_depth); rc = ipr_get_saved_dev_attr(dev, IPR_TCQ_ENABLED, temp); if (rc == RC_SUCCESS) sscanf(temp, "%d", &attr->tcq_enabled); rc = ipr_get_saved_dev_attr(dev, IPR_FORMAT_TIMEOUT, temp); if (rc == RC_SUCCESS) sscanf(temp, "%d", &attr->format_timeout); return 0; } /** * ipr_modify_ioa_attr - * @ioa: ipr ioa struct * @attr: ipr_ioa_attr struct * * Returns: * 0 **/ int ipr_modify_ioa_attr(struct ipr_ioa *ioa, struct ipr_ioa_attr *attr) { char temp[100]; int rc; rc = ipr_get_saved_ioa_attr(ioa, IPR_GSCSI_HA_ONLY, temp); if (rc == RC_SUCCESS) sscanf(temp, "%d", &attr->gscsi_only_ha); rc = ipr_get_saved_ioa_attr(ioa, IPR_DUAL_ADAPTER_ACTIVE_ACTIVE, temp); if (rc == RC_SUCCESS) sscanf(temp, "%d", &attr->active_active); return 0; } /** * ipr_set_dev_attr - * @dev: ipr dev struct * @attr: ipr_disk_attr struct * @save: save flag * * Returns: * 0 if success / non-zero on failure **/ int ipr_set_dev_attr(struct ipr_dev *dev, struct ipr_disk_attr *attr, int save) { struct ipr_disk_attr old_attr; char temp[100]; if (ipr_get_dev_attr(dev, &old_attr)) return -EIO; if (attr->queue_depth != old_attr.queue_depth) { sprintf(temp, "%d", attr->queue_depth); if (ipr_is_af_dasd_device(dev)) { if (ipr_setup_af_qdepth(dev, attr->queue_depth)) return -EIO; } else { if (ipr_write_dev_attr(dev, "queue_depth", temp)) return -EIO; } if (save) ipr_save_dev_attr(dev, IPR_QUEUE_DEPTH, temp, 1); } if (attr->format_timeout != old_attr.format_timeout) { if (ipr_is_af_dasd_device(dev)) { sprintf(temp, "%d", attr->format_timeout); if (ipr_set_dasd_timeouts(dev, attr->format_timeout)) return -EIO; if (save) ipr_save_dev_attr(dev, IPR_FORMAT_TIMEOUT, temp, 1); } } if (attr->tcq_enabled != old_attr.tcq_enabled) { if (!ipr_is_af_dasd_device(dev)) { if (set_tagged(dev, attr->tcq_enabled)) return -EIO; sprintf(temp, "%d", attr->tcq_enabled); if (save) ipr_save_dev_attr(dev, IPR_TCQ_ENABLED, temp, 1); } } return 0; } /** * ipr_set_ioa_attr - * @ioa: ipr ioa struct * @attr: ipr_ioa_attr struct * @save: save flag * * Returns: * 0 if success / non-zero on failure **/ int ipr_set_ioa_attr(struct ipr_ioa *ioa, struct ipr_ioa_attr *attr, int save) { struct ipr_ioa_attr old_attr; char temp[100]; int mode; if (ipr_get_ioa_attr(ioa, &old_attr)) return -EIO; /* FIXME - preferred_primary and active_active may change at the same * time. This code may need to change. */ if (attr->preferred_primary != old_attr.preferred_primary) if (ipr_change_multi_adapter_assignment(ioa, attr->preferred_primary, IPR_PRESERVE_ASYMMETRIC_STATE)) return -EIO; if (attr->gscsi_only_ha != old_attr.gscsi_only_ha) { sprintf(temp, "%d", attr->gscsi_only_ha); if (ipr_set_ha_mode(ioa, attr->gscsi_only_ha)) return -EIO; if (save) ipr_save_ioa_attr(ioa, IPR_GSCSI_HA_ONLY, temp, 1); ipr_reset_adapter(ioa); } if (attr->active_active != old_attr.active_active && ioa->asymmetric_access) { sprintf(temp, "%d", attr->active_active); /* If setting active/active, use mode page 24. * If clearing, reset the adapter and then use * Change Multi Adapter Assignment. */ if (attr->active_active) { if (ipr_set_active_active_mode(ioa)) return -EIO; if (save) ipr_save_ioa_attr(ioa, IPR_DUAL_ADAPTER_ACTIVE_ACTIVE, temp, 1); } else { if (save) ipr_save_ioa_attr(ioa, IPR_DUAL_ADAPTER_ACTIVE_ACTIVE, temp, 1); ipr_reset_adapter(ioa); if (ipr_change_multi_adapter_assignment(ioa, attr->preferred_primary, attr->active_active)) return -EIO; } } if (attr->caching != old_attr.caching) { if (attr->caching == IPR_IOA_REQUESTED_CACHING_DEFAULT) mode = IPR_IOA_SET_CACHING_DEFAULT; else mode = IPR_IOA_SET_CACHING_DISABLED; ipr_change_cache_parameters(ioa, mode); } get_dual_ioa_state(ioa); /* for preferred_primary */ get_subsys_config(ioa); /* for gscsi_only_ha */ return 0; } /** * ipr_query_dasd_timeouts - * @dev: ipr dev struct * @timeouts: ipr_query_dasd_timesouts struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_dasd_timeouts(struct ipr_dev *dev, struct ipr_query_dasd_timeouts *timeouts) { int fd, rc; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; char *name = dev->gen_name; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(timeouts, 0, sizeof(*timeouts)); memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = QUERY_DASD_TIMEOUTS; cdb[7] = (sizeof(*timeouts) >> 8) & 0xff; cdb[8] = sizeof(*timeouts) & 0xff; rc = sg_ioctl(fd, cdb, timeouts, sizeof(*timeouts), SG_DXFER_FROM_DEV, &sense_data, SET_DASD_TIMEOUTS_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Query DASD timeouts", rc); close(fd); return rc; } /** * ipr_get_bus_attr - * @ioa: ipr ioa struct * @sbus: ipr_scsi_buses struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_get_bus_attr(struct ipr_ioa *ioa, struct ipr_scsi_buses *sbus) { struct ipr_mode_pages mode_pages; struct ipr_mode_page_28 *page_28; struct ipr_mode_page_28_scsi_dev_bus_attr *bus; int rc, i, busno; memset(&mode_pages, 0, sizeof(mode_pages)); memset(sbus, 0, sizeof(*sbus)); rc = ipr_mode_sense(&ioa->ioa, 0x28, &mode_pages); if (rc) return rc; page_28 = (struct ipr_mode_page_28 *)(mode_pages.data + mode_pages.hdr.block_desc_len); for_each_bus_attr(bus, page_28, i) { busno = bus->res_addr.bus; sbus->bus[busno].max_xfer_rate = ntohl(bus->max_xfer_rate); sbus->bus[busno].qas_capability = bus->qas_capability; sbus->bus[busno].scsi_id = bus->scsi_id; sbus->bus[busno].bus_width = bus->bus_width; sbus->bus[busno].extended_reset_delay = bus->extended_reset_delay; sbus->bus[busno].min_time_delay = bus->min_time_delay; sbus->num_buses++; } return 0; } /** * ipr_set_bus_attr - * @ioa: ipr ioa struct * @sbus: ipr_scsi_buses struct * @save: save flag * * Returns: * 0 if success / non-zero on failure **/ int ipr_set_bus_attr(struct ipr_ioa *ioa, struct ipr_scsi_buses *sbus, int save) { struct ipr_mode_pages mode_pages; struct ipr_mode_page_28 *page_28; struct ipr_mode_page_28_scsi_dev_bus_attr *bus; struct ipr_scsi_buses old_settings; int rc, i, busno, len; int reset_needed = 0; char value_str[64]; memset(&mode_pages, 0, sizeof(mode_pages)); rc = ipr_mode_sense(&ioa->ioa, 0x28, &mode_pages); if (rc) return rc; rc = ipr_get_bus_attr(ioa, &old_settings); if (rc) return rc; page_28 = (struct ipr_mode_page_28 *) (mode_pages.data + mode_pages.hdr.block_desc_len); for_each_bus_attr(bus, page_28, i) { busno = bus->res_addr.bus; bus->bus_width = sbus->bus[busno].bus_width; if (save && bus->bus_width != old_settings.bus[busno].bus_width) { sprintf(value_str, "%d", bus->bus_width); ipr_save_bus_attr(ioa, i, IPR_BUS_WIDTH, value_str, 1); } bus->max_xfer_rate = htonl(sbus->bus[busno].max_xfer_rate); if (save && bus->max_xfer_rate != old_settings.bus[busno].max_xfer_rate) { sprintf(value_str, "%d", IPR_BUS_XFER_RATE_TO_THRUPUT(sbus->bus[busno].max_xfer_rate, bus->bus_width)); ipr_save_bus_attr(ioa, i, IPR_MAX_XFER_RATE_STR, value_str, 1); } bus->qas_capability = sbus->bus[busno].qas_capability; if (save && bus->qas_capability != old_settings.bus[busno].qas_capability) { sprintf(value_str, "%d", bus->qas_capability); ipr_save_bus_attr(ioa, i, IPR_QAS_CAPABILITY, value_str, 1); } if (bus->scsi_id != sbus->bus[busno].scsi_id) reset_needed = 1; bus->scsi_id = sbus->bus[busno].scsi_id; if (save && bus->scsi_id != old_settings.bus[busno].scsi_id) { sprintf(value_str, "%d", bus->scsi_id); ipr_save_bus_attr(ioa, i, IPR_HOST_SCSI_ID, value_str, 1); } bus->min_time_delay = sbus->bus[busno].min_time_delay; if (save && bus->min_time_delay != old_settings.bus[busno].min_time_delay) { sprintf(value_str, "%d", bus->min_time_delay); ipr_save_bus_attr(ioa, i, IPR_MIN_TIME_DELAY, value_str, 1); } bus->extended_reset_delay = sbus->bus[busno].extended_reset_delay; } len = mode_pages.hdr.length + 1; mode_pages.hdr.length = 0; rc = ipr_mode_select(&ioa->ioa, &mode_pages, len); if (reset_needed) ipr_reset_adapter(ioa); return rc; } /** * ipr_modify_bus_attr - * @ioa: ipr ioa struct * @sbus: ipr_scsi_buses struct * * Returns: * nothing **/ void ipr_modify_bus_attr(struct ipr_ioa *ioa, struct ipr_scsi_buses *sbus) { int i, rc, saved_value, max_xfer_rate; char value_str[64]; for (i = 0; i < sbus->num_buses; i++) { rc = ipr_get_saved_bus_attr(ioa, i, IPR_QAS_CAPABILITY, value_str); if (rc == RC_SUCCESS) { sscanf(value_str, "%d", &saved_value); sbus->bus[i].qas_capability = saved_value; } if (ioa->scsi_id_changeable) { rc = ipr_get_saved_bus_attr(ioa, i, IPR_HOST_SCSI_ID, value_str); if (rc == RC_SUCCESS) { sscanf(value_str, "%d", &saved_value); sbus->bus[i].scsi_id = saved_value; } } rc = ipr_get_saved_bus_attr(ioa, i, IPR_BUS_WIDTH, value_str); if (rc == RC_SUCCESS) { sscanf(value_str, "%d", &saved_value); sbus->bus[i].bus_width = saved_value; } max_xfer_rate = get_max_bus_speed(ioa, i); rc = ipr_get_saved_bus_attr(ioa, i, IPR_MAX_XFER_RATE_STR, value_str); if (rc == RC_SUCCESS) { sscanf(value_str, "%d", &saved_value); if (saved_value <= max_xfer_rate) { sbus->bus[i].max_xfer_rate = IPR_BUS_THRUPUT_TO_XFER_RATE(saved_value, sbus->bus[i].bus_width); } else { sbus->bus[i].max_xfer_rate = IPR_BUS_THRUPUT_TO_XFER_RATE(max_xfer_rate, sbus->bus[i].bus_width); sprintf(value_str, "%d", max_xfer_rate); ipr_save_bus_attr(ioa, i, IPR_MAX_XFER_RATE_STR, value_str, 1); } } else { sbus->bus[i].max_xfer_rate = IPR_BUS_THRUPUT_TO_XFER_RATE(max_xfer_rate, sbus->bus[i].bus_width); } rc = ipr_get_saved_bus_attr(ioa, i, IPR_MIN_TIME_DELAY, value_str); if (rc == RC_SUCCESS) { sscanf(value_str,"%d", &saved_value); sbus->bus[i].min_time_delay = saved_value; } else { sbus->bus[i].min_time_delay = IPR_INIT_SPINUP_DELAY; } } } /** * get_unsupp_af - * @ing: ipr_std_ing_data struct * @page3: ipr_dasd_inquiry_page3 struct * * Returns: * unsupported_af_dasd struct **/ struct unsupported_af_dasd * get_unsupp_af(struct ipr_std_inq_data *inq, struct ipr_dasd_inquiry_page3 *page3) { int i, j; for (i = 0; i < ARRAY_SIZE(unsupported_af); i++) { for (j = 0; j < IPR_VENDOR_ID_LEN; j++) { if (unsupported_af[i].compare_vendor_id_byte[j] && unsupported_af[i].vendor_id[j] != inq->vpids.vendor_id[j]) break; } if (j != IPR_VENDOR_ID_LEN) continue; for (j = 0; j < IPR_PROD_ID_LEN; j++) { if (unsupported_af[i].compare_product_id_byte[j] && unsupported_af[i].product_id[j] != inq->vpids.product_id[j]) break; } if (j != IPR_PROD_ID_LEN) continue; for (j = 0; j < 4; j++) { if (unsupported_af[i].lid_mask[j] && unsupported_af[i].lid[j] != page3->load_id[j]) break; } if (j != 4) continue; return &unsupported_af[i]; } return NULL; } /** * disk_needs_msl - * @unsupp_af: unsupported_af_dasd struct * @inq: ipr_std_inq_data struct * * Returns: * true if needs msl / false otherwise **/ bool disk_needs_msl(struct unsupported_af_dasd *unsupp_af, struct ipr_std_inq_data *inq) { u32 ros_rcv_ram_rsvd, min_ucode_level; int j; if (unsupp_af->supported_with_min_ucode_level) { min_ucode_level = 0; ros_rcv_ram_rsvd = 0; for (j = 0; j < 4; j++) { if (unsupp_af->min_ucode_mask[j]) { min_ucode_level = (min_ucode_level << 8) | unsupp_af->min_ucode_level[j]; ros_rcv_ram_rsvd = (ros_rcv_ram_rsvd << 8) | inq->ros_rsvd_ram_rsvd[j]; } } if (min_ucode_level > ros_rcv_ram_rsvd) return true; } return false; } /** * is_af_blocked - * @dev: ipr dev struct * @silent: * * Returns: * true if blocked / false otherwise **/ bool is_af_blocked(struct ipr_dev *dev, int silent) { int rc; struct ipr_std_inq_data std_inq_data; struct ipr_dasd_inquiry_page3 dasd_page3_inq; struct unsupported_af_dasd *unsupp_af; /* Zero out inquiry data */ memset(&std_inq_data, 0, sizeof(std_inq_data)); rc = ipr_inquiry(dev, IPR_STD_INQUIRY, &std_inq_data, sizeof(std_inq_data)); if (rc != 0) return false; /* Issue page 3 inquiry */ memset(&dasd_page3_inq, 0, sizeof(dasd_page3_inq)); rc = ipr_inquiry(dev, 0x03, &dasd_page3_inq, sizeof(dasd_page3_inq)); if (rc != 0) return false; unsupp_af = get_unsupp_af(&std_inq_data, &dasd_page3_inq); if (!unsupp_af) return false; /* If we make it this far, we have a match into the table. Now, determine if we need a certain level of microcode or if this disk is not supported all together. */ if (unsupp_af->supported_with_min_ucode_level) { if (disk_needs_msl(unsupp_af, &std_inq_data)) { if (ipr_force) { if (!silent) scsi_err(dev, "Disk %s needs updated microcode " "before transitioning to 522 bytes/sector " "format. IGNORING SINCE --force USED!", dev->gen_name); return false; } else { if (!silent) scsi_err(dev, "Disk %s needs updated microcode " "before transitioning to 522 bytes/sector " "format.", dev->gen_name); return true; } } } else {/* disk is not supported at all */ if (!silent) syslog(LOG_ERR,"Disk %s canot be formatted to " "522 bytes/sector.", dev->gen_name); return true; } return false; } /** * ipr_read_dev_attr - * @dev: ipr dev struct * @attr: * @value: * * Returns: * 0 if success / non-zero on failure **/ int ipr_read_dev_attr(struct ipr_dev *dev, char *attr, char *value, size_t value_len) { char *sysfs_dev_name; char devpath[PATH_MAX]; ssize_t len; if (!dev->scsi_dev_data) { scsi_dbg(dev, "Cannot read dev attr %s. NULL scsi data\n", attr); return -ENOENT; } sysfs_dev_name = dev->scsi_dev_data->sysfs_device_name; sprintf(devpath, "/sys/class/scsi_device/%s/device", sysfs_dev_name); len = sysfs_read_attr(devpath, attr, value, value_len); if (len < 0) { scsi_dbg(dev, "Failed to read %s attribute. %m\n", attr); return -EIO; } return 0; } /** * ipr_write_dev_attr - * @dev: ipr dev struct * @attr: * @value: * * Returns: * 0 if success / non-zero on failure **/ int ipr_write_dev_attr(struct ipr_dev *dev, char *attr, char *value) { char *sysfs_dev_name; char devpath[PATH_MAX]; if (!dev->scsi_dev_data) return -ENOENT; sysfs_dev_name = dev->scsi_dev_data->sysfs_device_name; sprintf(devpath, "/sys/class/scsi_device/%s/device", sysfs_dev_name); if (sysfs_write_attr(devpath, attr, value, strlen(value)) < 0) { printf("Failed to write attribute: %s\n", attr); return -EIO; } return 0; } /** * get_ucode_date - * @ucode_file: microcode file name * @ucode_date: microcode date string pointer * @max_size: max size for the date field * * Returns: * nothing **/ void get_ucode_date(char *ucode_file, char *ucode_date, int max_size) { struct stat st; struct tm *file_tm; ucode_date[0] = '\0'; if (stat(ucode_file, &st)) return; file_tm = localtime(&st.st_mtime); if (!file_tm) return; strftime(ucode_date, max_size, "%D", file_tm); } /** * get_ioa_ucode_version - * @ucode_file: microcode file name * * Returns: * 0 if success / non-zero on failure **/ u32 get_ioa_ucode_version(char *ucode_file) { int fd, rc; struct stat ucode_stats; struct ipr_ioa_ucode_header *image_hdr; fd = open(ucode_file, O_RDONLY); if (fd == -1) return 0; rc = fstat(fd, &ucode_stats); if (rc != 0) { close(fd); return 0; } image_hdr = mmap(NULL, ucode_stats.st_size, PROT_READ, MAP_SHARED, fd, 0); if (image_hdr == MAP_FAILED) { close(fd); return 0; } rc = ntohl(image_hdr->rev_level); munmap(image_hdr, ucode_stats.st_size); close(fd); return rc; } /** * fw_compare - compare two firmware images * @parm1: pointer to firmware image * @parm2: pointer to firmware image * * Returns: * 0 if the images are the same / non-zero otherwise **/ /* Sort in decending order */ static int fw_compare(const void *parm1, const void *parm2) { struct ipr_fw_images *first = (struct ipr_fw_images *)parm1; struct ipr_fw_images *second = (struct ipr_fw_images *)parm2; return memcmp(&second->version, &first->version, sizeof(second->version)); } /** * ipr_get_hotplug_dir - * * Returns: * 0 if success / non-zero on failure **/ static int ipr_get_hotplug_dir() { FILE *file; char buf[100]; char *loc, *end; file = fopen(FIRMWARE_HOTPLUG_CONFIG_FILE, "r"); if (!file) { hotplug_dir = realloc(hotplug_dir, strlen(FIRMWARE_HOTPLUG_DEFAULT_DIR) + 1); if (!hotplug_dir) return -ENOMEM; strcpy(hotplug_dir, FIRMWARE_HOTPLUG_DEFAULT_DIR); return 0; } clearerr(file); do { if (feof(file)) { syslog(LOG_ERR, "Failed parsing %s. Reached end of file.\n", FIRMWARE_HOTPLUG_CONFIG_FILE); return -EIO; } fgets(buf, 100, file); loc = strstr(buf, FIRMWARE_HOTPLUG_DIR_TAG); } while(!loc || buf[0] == '#'); loc = strchr(buf, '/'); fclose(file); if (!loc) { syslog(LOG_ERR, "Failed parsing %s.\n", FIRMWARE_HOTPLUG_CONFIG_FILE); return -EIO; } end = strchr(loc, ' '); if (!end) end = strchr(loc, '"'); if (end) *end = '\0'; hotplug_dir = realloc(hotplug_dir, strlen(loc) + 1); if (!hotplug_dir) return -ENOMEM; strcpy(hotplug_dir, loc); end = strchr(hotplug_dir, '\n'); if (end) *end = '\0'; return 0; } /** * get_dasd_ucode_version - * @ucode_file: file name of microcode file * @has_hdr: has header flag * * Returns: * 0 if success / non-zero on failure **/ u32 get_dasd_ucode_version(char *ucode_file, int has_hdr) { int fd; unsigned int len; struct stat ucode_stats; struct ipr_dasd_ucode_header *hdr; char *tmp; u32 rc; if (has_hdr) { fd = open(ucode_file, O_RDONLY); if (fd == -1) return 0; rc = fstat(fd, &ucode_stats); if (rc != 0) { fprintf(stderr, "Failed to stat %s\n", ucode_file); close(fd); return 0; } hdr = mmap(NULL, ucode_stats.st_size, PROT_READ, MAP_SHARED, fd, 0); if (hdr == MAP_FAILED) { fprintf(stderr, "mmap of %s failed\n", ucode_file); close(fd); return 0; } len = (hdr->length[0] << 16) | (hdr->length[1] << 8) | hdr->length[2]; if (len == ucode_stats.st_size) { rc = (hdr->modification_level[0] << 24) | (hdr->modification_level[1] << 16) | (hdr->modification_level[2] << 8) | hdr->modification_level[3]; munmap(hdr, ucode_stats.st_size); close(fd); return rc; } else { munmap(hdr, ucode_stats.st_size); close(fd); } } tmp = strrchr(ucode_file, '.'); if (!tmp) return 0; rc = strtoul(tmp+1, NULL, 16); return rc; } /** * get_ses_ucode_version - * @ucode_file: * * Returns: * ses microcode version / 0 if failure **/ u32 get_ses_ucode_version(char *ucode_file) { char *tmp = strrchr(ucode_file, '.'); if (!tmp) return 0; if (strlen(tmp+1) < 4) return 0; return (tmp[1] << 24) | (tmp[2] << 16) | (tmp[3] << 8) | tmp[4]; } /** * get_dev_fw_version - * @dev: ipr dev struct * * Returns: * device firmware version / 0 if failure **/ u32 get_dev_fw_version(struct ipr_dev *dev) { u8 release_level[4]; int rc; rc = ipr_get_fw_version(dev, release_level); if (rc != 0) { scsi_dbg(dev, "Inquiry failed\n"); return 0; } rc = release_level[0] << 24 | release_level[1] << 16 | release_level[2] << 8 | release_level[3]; return rc; } /** * get_ioa_fw_version - * @ioa: ipr ioa struct * * Returns: * ioa firmware version **/ u32 get_ioa_fw_version(struct ipr_ioa *ioa) { char devpath[PATH_MAX]; char value[16]; ssize_t len; u32 fw_version; sprintf(devpath, "/sys/class/scsi_host/host%s", ioa->host_name); len = sysfs_read_attr(devpath, "fw_version", value, 16); if (len < 0) return -1; sscanf(value, "%8X", &fw_version); return fw_version; } /** * get_ioa_image_type - * @ioa: ipr ioa struct * * Returns: * ioa image type **/ static u8 get_ioa_image_type(struct ipr_ioa *ioa) { u32 fw_version = get_ioa_fw_version(ioa); return ((fw_version & 0x00ff0000) >> 16); } /** * get_ioa_fw_name - * @ioa: ipr ioa struct * @buf: data buffer * * Returns: * nothing **/ static void get_ioa_fw_name(struct ipr_ioa *ioa, char *buf) { const struct ioa_parms *ioa_parms = get_ioa_fw(ioa); if (ioa_parms) strcpy(buf, ioa_parms->fw_name); else sprintf(buf, "534953%02X", get_ioa_image_type(ioa)); } /** * get_linux_ioa_fw_name - * @ioa: ipr ioa struct * @buf: data buffer * * Returns: * nothing **/ static void get_linux_ioa_fw_name(struct ipr_ioa *ioa, char *buf) { sprintf(buf, "pci.%04X%04X.%02X", ioa->pci_vendor, ioa->pci_device, get_ioa_image_type(ioa)); } /** * init_ioa_ucode_entry - * @img: ipr_fw_images struct * * Returns: * nothing **/ static void init_ioa_ucode_entry(struct ipr_fw_images *img) { img->version = get_ioa_ucode_version(img->file); img->has_header = 0; get_ucode_date(img->file, img->date, sizeof(img->date)); } /** * init_disk_ucode_entry - * @img: ipr_fw_images * * Returns: * nothing **/ static void init_disk_ucode_entry(struct ipr_fw_images *img) { img->version = get_dasd_ucode_version(img->file, 1); img->has_header = 1; get_ucode_date(img->file, img->date, sizeof(img->date)); } /** * init_disk_ucode_entry_nohdr - * @img: ipr_fw_images struct * * Returns: * nothing **/ static void init_disk_ucode_entry_nohdr(struct ipr_fw_images *img) { img->version = get_dasd_ucode_version(img->file, 0); img->has_header = 0; get_ucode_date(img->file, img->date, sizeof(img->date)); } /** * init_ses_ucode_entry_nohdr - * @img: ipr_fw_images struct * * Returns: * nothing **/ static void init_ses_ucode_entry_nohdr(struct ipr_fw_images *img) { img->version = get_ses_ucode_version(img->file); img->has_header = 0; get_ucode_date(img->file, img->date, sizeof(img->date)); } /** * scan_fw_dir - * @path: * @name: * @list: * @len: * @init function pointer to initialization function * * Returns: * int len **/ static int scan_fw_dir(char *path, char *name, struct ipr_fw_images **list, int len, void (*init)(struct ipr_fw_images *)) { struct dirent **dirent; int rc, i; struct ipr_fw_images *ret = *list; rc = scandir(path, &dirent, NULL, alphasort); for (i = 0; i < rc && rc > 0; i++) { if (strstr(dirent[i]->d_name, name) == dirent[i]->d_name) { ret = realloc(ret, sizeof(*ret) * (len + 1)); sprintf(ret[len].file, "%s/%s", path, dirent[i]->d_name); init(&ret[len]); len++; } } for (i = 0; i < rc; i++) free(dirent[i]); if (rc > 0) free(dirent); *list = ret; return len; } /** * get_ioa_firmware_image_list - * @ioa: ipr ioa struct * @list: ipr_fw_images struct * * Returns: * length of list **/ int get_ioa_firmware_image_list(struct ipr_ioa *ioa, struct ipr_fw_images **list) { char buf[100]; struct ipr_fw_images *ret = NULL; int len = 0; *list = NULL; get_ioa_fw_name(ioa, buf); len = scan_fw_dir(UCODE_BASE_DIR, buf, &ret, len, init_ioa_ucode_entry); get_linux_ioa_fw_name(ioa, buf); len = scan_fw_dir(LINUX_UCODE_BASE_DIR, buf, &ret, len, init_ioa_ucode_entry); sprintf(buf, "ibmsis%X.img", ioa->ccin); len = scan_fw_dir("/etc/microcode", buf, &ret, len, init_ioa_ucode_entry); sprintf(buf, "ibmsis%X.img", get_ioa_image_type(ioa)); len = scan_fw_dir("/etc/microcode", buf, &ret, len, init_ioa_ucode_entry); if (ret) qsort(ret, len, sizeof(*ret), fw_compare); *list = ret; return len; } /** * get_dasd_firmware_image_list- * @dev: ipr dev struct * @list: irp_fw_images struct * * Returns: * length value if success / non-zero on failure **/ int get_dasd_firmware_image_list(struct ipr_dev *dev, struct ipr_fw_images **list) { char buf[100]; struct ipr_fw_images *ret = NULL; struct ipr_dasd_inquiry_page3 page3_inq; int rc; int len = 0; *list = NULL; memset(&page3_inq, 0, sizeof(page3_inq)); rc = ipr_inquiry(dev, 3, &page3_inq, sizeof(page3_inq)); if (rc != 0) { scsi_dbg(dev, "Inquiry failed\n"); return -EIO; } sprintf(buf, "%.7s.%02X%02X%02X%02X", dev->scsi_dev_data->product_id, page3_inq.load_id[0], page3_inq.load_id[1], page3_inq.load_id[2], page3_inq.load_id[3]); len = scan_fw_dir(UCODE_BASE_DIR, buf, &ret, len, init_disk_ucode_entry_nohdr); if (memcmp(dev->scsi_dev_data->vendor_id, "IBMAS400", 8) == 0) sprintf(buf, "ibmsis%02X%02X%02X%02X.img", page3_inq.load_id[0], page3_inq.load_id[1], page3_inq.load_id[2], page3_inq.load_id[3]); len = scan_fw_dir("/etc/microcode/device", buf, &ret, len, init_disk_ucode_entry); sprintf(buf, "IBM-%.7s.%02X%02X%02X%02X", dev->scsi_dev_data->product_id, page3_inq.load_id[0], page3_inq.load_id[1], page3_inq.load_id[2], page3_inq.load_id[3]); len = scan_fw_dir(LINUX_UCODE_BASE_DIR, buf, &ret, len, init_disk_ucode_entry_nohdr); if (len) qsort(ret, len, sizeof(*ret), fw_compare); else scsi_dbg(dev, "Could not find device firmware file\n"); *list = ret; return len; } /** * get_ses_load_id- * @dev: ipr dev struct * @load_id: * * Returns: * 0 if success / non-zero on failure **/ static int get_ses_load_id(struct ipr_dev *dev, u8 load_id[4]) { struct ipr_dasd_inquiry_page3 page3_inq; int rc; memset(&page3_inq, 0, sizeof(page3_inq)); rc = ipr_inquiry(dev, 3, &page3_inq, sizeof(page3_inq)); if (rc) return rc; if (ipr_is_ses(dev) && !__ioa_is_spi(dev->ioa)) memcpy(load_id, page3_inq.release_level, 4); else memcpy(load_id, page3_inq.load_id, 4); return 0; } /** * get_ses_firmware_image_list- * @dev: ipr dev struct * @list: * * Returns: * length value if success / non-zero on failure **/ int get_ses_firmware_image_list(struct ipr_dev *dev, struct ipr_fw_images **list) { char buf[100]; struct ipr_fw_images *ret = NULL; u8 load_id[4]; int rc; int len = 0; *list = NULL; rc = get_ses_load_id(dev, load_id); if (rc != 0) { scsi_dbg(dev, "Inquiry failed\n"); return -EIO; } sprintf(buf, "%02X%02X%02X%02X", load_id[0], load_id[1], load_id[2], load_id[3]); len = scan_fw_dir(UCODE_BASE_DIR, buf, &ret, len, init_ses_ucode_entry_nohdr); sprintf(buf, "IBM-%02X%02X%02X%02X", load_id[0], load_id[1], load_id[2], load_id[3]); len = scan_fw_dir(LINUX_UCODE_BASE_DIR, buf, &ret, len, init_ses_ucode_entry_nohdr); if (len) qsort(ret, len, sizeof(*ret), fw_compare); else scsi_dbg(dev, "Could not find device firmware file\n"); *list = ret; return len; } struct ipr_ioa_desc { u16 type; const char *desc; }; struct ipr_ioa_desc ioa_desc [] = { {0x5702, "PCI-X Dual Channel Ultra320 SCSI Adapter [5702]"}, {0x1974, "PCI-X Dual Channel Ultra320 SCSI Adapter [1974]"}, {0x5703, "PCI-X Dual Channel Ultra320 SCSI RAID Adapter [5703]"}, {0x1975, "PCI-X Dual Channel Ultra320 SCSI RAID Adapter [1975]"}, {0x2780, "PCI-X Quad Channel Ultra320 SCSI RAID Adapter [2780]"}, {0x5709, "SCSI RAID Enablement Card for PCI-X Dual Channel Ultra320 SCSI Integrated Controller [5709]"}, {0x1976, "SCSI RAID Enablement Card for PCI-X Dual Channel Ultra320 SCSI Integrated Controller [1976]"}, {0x570A, "PCI-X Dual Channel SCSI Integrated Controller (Adapter bus) [570A]"}, {0x570B, "PCI-X Dual Channel SCSI Integrated Controller (Adapter bus) [570B]"} }; /** * get_long_ioa_desc- * @type: * * Returns: * IOA description if success / NULL on failure **/ static const char *get_long_ioa_desc(u16 type) { int i; for (i = 0; i < sizeof(ioa_desc)/sizeof(ioa_desc[0]); i++) { if (type == ioa_desc[i].type) return ioa_desc[i].desc; } return NULL; } /** * ipr_log_ucode_error - log a microcode error * @ioa: ipr ioa struct * * Returns: * nothing **/ void ipr_log_ucode_error(struct ipr_ioa *ioa) { const char *desc = get_long_ioa_desc(ioa->ccin); if (desc) { syslog(LOG_ERR, "Could not find required level of microcode for IBM '%s'. " "Please download the latest microcode from " "http://techsupport.services.ibm.com/server/mdownload/download.html. " "SCSI speeds will be limited to %d MB/s until updated microcode is downloaded.\n", desc, IPR_SAFE_XFER_RATE); } else { syslog(LOG_ERR, "Could not find required level of microcode for IBM %04X. " "Please download the latest microcode from " "http://techsupport.services.ibm.com/server/mdownload/download.html. " "SCSI speeds will be limited to %d MB/s until updated microcode is downloaded.\n", ioa->ccin, IPR_SAFE_XFER_RATE); } } /** * ipr_update_ioa_fw - * @ioa: ipr ioa struct * @image: pointer to fw image * @force: force flag * * Returns: * 0 if success / non-zero on failure **/ int ipr_update_ioa_fw(struct ipr_ioa *ioa, struct ipr_fw_images *image, int force) { struct ipr_ioa_ucode_header *image_hdr; struct ipr_ioa_ucode_ext_header *ext_hdr; struct ipr_ioa_ucode_img_desc *img_desc; struct stat ucode_stats; u32 fw_version; int fd, rc; int ioafw = 1; int host_num = ioa->host_num; char *tmp; char ucode_file[200]; DIR *dir; char *img_file; char cwd[200]; char devpath[PATH_MAX]; ssize_t len; fw_version = get_ioa_fw_version(ioa); if (fw_version >= ioa->msl && !force) return 0; if (ipr_get_hotplug_dir()) return 0; fd = open(image->file, O_RDONLY); if (fd < 0) { syslog(LOG_ERR, "Could not open firmware file %s.\n", image->file); return fd; } rc = fstat(fd, &ucode_stats); if (rc != 0) { syslog(LOG_ERR, "Failed to stat IOA firmware file: %s.\n", image->file); close(fd); return rc; } image_hdr = mmap(NULL, ucode_stats.st_size, PROT_READ, MAP_SHARED, fd, 0); if (image_hdr == MAP_FAILED) { syslog(LOG_ERR, "Error mapping IOA firmware file: %s.\n", image->file); close(fd); return -EIO; } ext_hdr = (void *)image_hdr + ntohl(image_hdr->header_length); img_desc = (void *)ext_hdr + ntohl(ext_hdr->img_desc_offset); if (strncmp(img_desc->fw_type, IPR_IOAF_STR, 4)) ioafw = 0; if (ntohl(image_hdr->rev_level) > fw_version || force) { if (ioafw) ioa_info(ioa, "Updating microcode from %08X to %08X.\n", fw_version, ntohl(image_hdr->rev_level)); else ioa_info(ioa, "Updating microcode to %08X.\n", ntohl(image_hdr->rev_level)); /* Give the file name an absolute path if needed. */ if (image->file[0] != '/') { getcwd(cwd, sizeof(cwd)); strcat(cwd, "/"); img_file = strcat(cwd, image->file); } else img_file = image->file; tmp = strrchr(img_file, '/'); if (tmp) tmp++; else { syslog(LOG_ERR, "Failed to find image name in %s\n", img_file); return -EIO; } dir = opendir(hotplug_dir); if (!dir) mkdir(hotplug_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); dir = opendir(hotplug_dir); if (!dir) { syslog(LOG_ERR, "Failed to open %s. %m\n", hotplug_dir); munmap(image_hdr, ucode_stats.st_size); close(fd); return -EIO; } closedir(dir); sprintf(ucode_file, "%s/.%s", hotplug_dir, tmp); symlink(img_file, ucode_file); sprintf(ucode_file, ".%s\n", tmp); sprintf(devpath, "/sys/class/scsi_host/%s", ioa->host_name); len = sysfs_write_attr(devpath, "update_fw", ucode_file, strlen(ucode_file)); sprintf(ucode_file, "%s/.%s", hotplug_dir, tmp); unlink(ucode_file); if (len < 0) ioa_err(ioa, "Microcode update failed. rc=%d\n", (int)len); check_current_config(false); for_each_ioa(ioa) { if (ioa->host_num != host_num) continue; ipr_init_ioa(ioa); break; } } else ipr_log_ucode_error(ioa); munmap(image_hdr, ucode_stats.st_size); close(fd); return rc; } /** * ipr_update_disk_fw - update disk fw * @dev: ipr dev struct * @image: pointer to fw image * @force: force flag * * Returns: * 0 if success / non-zero on failure **/ /* xxx TODO make a general routine to do write buffer that takes a struct ipr_fw_images as input */ int ipr_update_disk_fw(struct ipr_dev *dev, struct ipr_fw_images *image, int force) { int rc = 0; struct stat ucode_stats; int fd; struct ipr_dasd_ucode_header *img_hdr; struct ipr_dasd_inquiry_page3 page3_inq; struct ipr_std_inq_data std_inq_data; struct unsupported_af_dasd *unsupp_af; u32 level; u8 release_level[4]; memset(&std_inq_data, 0, sizeof(std_inq_data)); rc = ipr_inquiry(dev, IPR_STD_INQUIRY, &std_inq_data, sizeof(std_inq_data)); if (rc != 0) return rc; rc = ipr_inquiry(dev, 3, &page3_inq, sizeof(page3_inq)); if (rc != 0) { scsi_dbg(dev, "Inquiry failed\n"); return rc; } if (!force) { unsupp_af = get_unsupp_af(&std_inq_data, &page3_inq); if (!unsupp_af) return 0; if (!disk_needs_msl(unsupp_af, &std_inq_data)) return 0; } fd = open(image->file, O_RDONLY); if (fd < 0) { syslog_dbg("Could not open firmware file %s.\n", image->file); return fd; } rc = fstat(fd, &ucode_stats); if (rc != 0) { syslog(LOG_ERR, "Failed to stat firmware file: %s.\n", image->file); close(fd); return rc; } img_hdr = mmap(NULL, ucode_stats.st_size, PROT_READ, MAP_SHARED, fd, 0); if (img_hdr == MAP_FAILED) { syslog(LOG_ERR, "Error reading firmware file: %s.\n", image->file); close(fd); return -EIO; } level = htonl(image->version); __ipr_get_fw_version(dev, &page3_inq, release_level); if (memcmp(&level, page3_inq.release_level, 4) > 0 || force) { scsi_info(dev, "Updating device microcode using %s " "from %02X%02X%02X%02X (%c%c%c%c) to %08X (%c%c%c%c)\n", image->file, release_level[0], release_level[1], release_level[2], release_level[3], release_level[0], release_level[1], release_level[2], release_level[3], image->version, image->version >> 24, (image->version >> 16) & 0xff, (image->version >> 8) & 0xff, image->version & 0xff); rc = ipr_write_buffer(dev, img_hdr, ucode_stats.st_size); ipr_init_dev(dev); } if (munmap(img_hdr, ucode_stats.st_size)) syslog(LOG_ERR, "munmap failed.\n"); close(fd); return rc; } /** * mode_select - issue a mode select command * @dev: ipr dev struct * @buff: data buffer * @length: * * Returns: * 0 if success / non-zero on failure **/ static int mode_select(struct ipr_dev *dev, void *buff, int length) { int fd; u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc; char *name = dev->gen_name; fd = open(name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = MODE_SELECT; cdb[1] = 0x11; cdb[4] = length; rc = sg_ioctl(fd, cdb, buff, length, SG_DXFER_TO_DEV, &sense_data, IPR_INTERNAL_TIMEOUT); if (rc) scsi_cmd_err(dev, &sense_data, "Mode Select", rc); close(fd); return rc; } /** * setup_page0x00 - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ static int setup_page0x00(struct ipr_dev *dev) { struct ipr_mode_pages mode_pages, ch_mode_pages; struct ipr_vendor_mode_page *page, *ch_page; int len; if (strncmp(dev->scsi_dev_data->vendor_id, "IBM", 3)) { scsi_dbg(dev, "Not setting up mode page 0x00 for unknown device.\n"); return 0; } memset(&mode_pages, 0, sizeof(mode_pages)); memset(&ch_mode_pages, 0, sizeof(ch_mode_pages)); if (ipr_mode_sense(dev, 0x00, &mode_pages)) return -EIO; if (ipr_mode_sense(dev, 0x40, &ch_mode_pages)) return -EIO; page = (struct ipr_vendor_mode_page *)(((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); ch_page = (struct ipr_vendor_mode_page *)(((u8 *)&ch_mode_pages) + ch_mode_pages.hdr.block_desc_len + sizeof(ch_mode_pages.hdr)); IPR_SET_MODE(ch_page->arhes, page->arhes, 1); IPR_SET_MODE(ch_page->cmdac, page->cmdac, 1); IPR_SET_MODE(ch_page->caen, page->caen, 1); /* Use a 3 second command aging timer - units are 50 ms */ IPR_SET_MODE(ch_page->cmd_aging_limit_hi, page->cmd_aging_limit_hi, 0); IPR_SET_MODE(ch_page->cmd_aging_limit_lo, page->cmd_aging_limit_lo, 60); len = mode_pages.hdr.length + 1; mode_pages.hdr.length = 0; mode_pages.hdr.medium_type = 0; mode_pages.hdr.device_spec_parms = 0; page->hdr.parms_saveable = 0; if (mode_select(dev, &mode_pages, len)) { scsi_dbg(dev, "Failed to setup mode page 0x00\n"); return -EIO; } return 0; } /** * setup_page0x01 - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ static int setup_page0x01(struct ipr_dev *dev) { struct ipr_mode_pages mode_pages, ch_mode_pages; struct ipr_rw_err_mode_page *page, *ch_page; int len; memset(&mode_pages, 0, sizeof(mode_pages)); memset(&ch_mode_pages, 0, sizeof(ch_mode_pages)); if (ipr_mode_sense(dev, 0x01, &mode_pages)) return -EIO; if (ipr_mode_sense(dev, 0x41, &ch_mode_pages)) return -EIO; page = (struct ipr_rw_err_mode_page *)(((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); ch_page = (struct ipr_rw_err_mode_page *)(((u8 *)&ch_mode_pages) + ch_mode_pages.hdr.block_desc_len + sizeof(ch_mode_pages.hdr)); IPR_SET_MODE(ch_page->awre, page->awre, 1); IPR_SET_MODE(ch_page->arre, page->arre, 1); if (page->awre != 1) goto error; if (page->arre != 1) goto error; len = mode_pages.hdr.length + 1; mode_pages.hdr.length = 0; mode_pages.hdr.medium_type = 0; mode_pages.hdr.device_spec_parms = 0; page->hdr.parms_saveable = 0; if (mode_select(dev, &mode_pages, len)) { error: scsi_err(dev, "Failed to setup mode page 0x01\n"); return -EIO; } return 0; } /** * setup_page0x0a - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ static int setup_page0x0a(struct ipr_dev *dev) { struct ipr_mode_pages mode_pages, ch_mode_pages; struct ipr_control_mode_page *page, *ch_page; int len; int rc = 0; memset(&mode_pages, 0, sizeof(mode_pages)); memset(&ch_mode_pages, 0, sizeof(ch_mode_pages)); if (ipr_mode_sense(dev, 0x0A, &mode_pages)) return -EIO; if (ipr_mode_sense(dev, 0x4A, &ch_mode_pages)) return -EIO; page = (struct ipr_control_mode_page *)(((u8 *)&mode_pages) + mode_pages.hdr.block_desc_len + sizeof(mode_pages.hdr)); ch_page = (struct ipr_control_mode_page *)(((u8 *)&ch_mode_pages) + ch_mode_pages.hdr.block_desc_len + sizeof(ch_mode_pages.hdr)); IPR_SET_MODE(ch_page->queue_algorithm_modifier, page->queue_algorithm_modifier, 1); IPR_SET_MODE(ch_page->tst, page->tst, 1); IPR_SET_MODE(ch_page->tas, page->tas, 1); switch(dev->ioa->tcq_mode) { case IPR_TCQ_FROZEN: IPR_SET_MODE(ch_page->qerr, page->qerr, 3); if (page->qerr != 3) { IPR_SET_MODE(ch_page->qerr, page->qerr, 1); if (page->tst != 1) IPR_SET_MODE(ch_page->tst, page->tst, 0); } scsi_dbg(dev, "Using control mode settings: " "TST=%d, QERR=%d, TAS=%d\n", page->tst, page->qerr, page->tas); break; case IPR_TCQ_NACA: IPR_SET_MODE(ch_page->qerr, page->qerr, 0); break; case IPR_TCQ_DISABLE: default: rc = -EIO; break; }; IPR_SET_MODE(ch_page->dque, page->dque, 0); if (page->dque != 0) { scsi_dbg(dev, "Cannot set dque=0\n"); return -EIO; } if (page->queue_algorithm_modifier != 1) scsi_dbg(dev, "Cannot set QAM=1\n"); len = mode_pages.hdr.length + 1; mode_pages.hdr.length = 0; mode_pages.hdr.medium_type = 0; mode_pages.hdr.device_spec_parms = 0; page->hdr.parms_saveable = 0; if (mode_select(dev, &mode_pages, len)) { scsi_err(dev, "Failed to setup mode page 0x0A\n"); return -EIO; } return rc; } /** * init_vset_dev - * @dev: ipr dev struct * * Returns: * nothing **/ /* * VSETs: * 1. Adjust queue depth based on number of devices * */ static void init_vset_dev(struct ipr_dev *dev) { struct ipr_query_res_state res_state; char q_depth[100]; char cur_depth[100], saved_depth[100]; int depth, rc; memset(&res_state, 0, sizeof(res_state)); if (polling_mode && !dev->should_init) return; if (!ipr_query_resource_state(dev, &res_state)) { depth = res_state.vset.num_devices_in_vset * 4; snprintf(q_depth, sizeof(q_depth), "%d", depth); q_depth[sizeof(q_depth)-1] = '\0'; if (ipr_read_dev_attr(dev, "queue_depth", cur_depth, 100)) return; rc = ipr_get_saved_dev_attr(dev, IPR_QUEUE_DEPTH, saved_depth); if (rc == RC_SUCCESS) { if (!strcmp(saved_depth, q_depth)) return; depth = strtoul(saved_depth, NULL, 10); if (depth && depth <= 255) strcpy(q_depth, saved_depth); } if (!strcmp(cur_depth, q_depth)) return; if (ipr_write_dev_attr(dev, "queue_depth", q_depth)) return; } } /** * init_gpdd_dev - * @dev: ipr dev struct * * Returns: * nothing **/ /* * GPDD DASD: * 1. Setup Mode Page 0x0A for TCQing. * 2. Enable TCQing * 3. Adjust queue depth * */ static void init_gpdd_dev(struct ipr_dev *dev) { struct ipr_disk_attr attr; struct sense_data_t sense_data; int rc; if (polling_mode && !dev->should_init) return; if (__ipr_test_unit_ready(dev, &sense_data)) { if ((sense_data.sense_key != UNIT_ATTENTION) || __ipr_test_unit_ready(dev, &sense_data)) return; } if (enable_af(dev)) return; if (ipr_get_dev_attr(dev, &attr)) return; if (setup_page0x00(dev)) return; if ((rc = setup_page0x0a(dev))) { if (rc != -EINVAL) { scsi_dbg(dev, "Failed to enable TCQing.\n"); return; } } else { attr.queue_depth = get_tcq_depth(dev); attr.tcq_enabled = 1; } if (ipr_modify_dev_attr(dev, &attr)) return; if (ipr_set_dev_attr(dev, &attr, 0)) return; } /** * init_af_dev - * @dev: ipr dev struct * * Returns: * nothing **/ /* * AF DASD: * 1. Setup mode pages (pages 0x01, 0x0A, 0x20) * 2. Send set supported devices * 3. Set DASD timeouts */ static void init_af_dev(struct ipr_dev *dev) { struct ipr_disk_attr attr; int rc; if (ipr_set_dasd_timeouts(dev, 0)) return; if (polling_mode && (!dev->should_init && !memcmp(&attr, &dev->attr, sizeof(attr)))) return; if (polling_mode && !dev_init_allowed(dev)) return; if (setup_page0x00(dev)) return; if (setup_page0x01(dev)) return; if (enable_af(dev)) return; if (ipr_get_dev_attr(dev, &attr)) return; if ((rc = setup_page0x0a(dev))) { if (rc != -EINVAL) { scsi_dbg(dev, "Failed to enable TCQing.\n"); return; } } else attr.queue_depth = get_tcq_depth(dev); if (ipr_modify_dev_attr(dev, &attr)) return; memcpy(&dev->attr, &attr, sizeof(attr)); if (ipr_set_dev_attr(dev, &attr, 0)) return; } /** * init_ioa_dev - * @dev: ipr dev struct * * Returns: * nothing **/ /* * IOA: * 1. Load saved adapter configuration */ static void init_ioa_dev(struct ipr_dev *dev) { struct ipr_scsi_buses buses; struct ipr_ioa_attr attr; if (!dev->ioa) return; if (polling_mode && !dev->ioa->should_init) return; if (ipr_get_ioa_attr(dev->ioa, &attr)) return; if (dev->ioa->asymmetric_access && dev->ioa->sis64) attr.active_active = 1; ipr_modify_ioa_attr(dev->ioa, &attr); if (ipr_set_ioa_attr(dev->ioa, &attr, 0)) return; if (ipr_get_bus_attr(dev->ioa, &buses)) return; ipr_modify_bus_attr(dev->ioa, &buses); if (ipr_set_bus_attr(dev->ioa, &buses, 0)) return; } /** * ipr_init_dev - * @dev: ipr dev struct * * Returns: * 0 **/ int ipr_init_dev(struct ipr_dev *dev) { if (!dev->scsi_dev_data) return 0; switch (dev->scsi_dev_data->type) { case TYPE_DISK: if (ipr_is_volume_set(dev)) init_vset_dev(dev); else init_gpdd_dev(dev); break; case IPR_TYPE_AF_DISK: init_af_dev(dev); break; case IPR_TYPE_ADAPTER: if (&dev->ioa->ioa == dev) init_ioa_dev(dev); break; default: break; }; return 0; } /** * ipr_init_new_dev - * @dev: ipr dev struct * * Returns: * nothing **/ int ipr_init_new_dev(struct ipr_dev *dev) { if (!dev->scsi_dev_data) return 0; switch (dev->scsi_dev_data->type) { case TYPE_DISK: if (!strlen(dev->dev_name)) return 1; wait_for_dev(dev->dev_name); break; case IPR_TYPE_ADAPTER: if (&dev->ioa->ioa != dev) break; case IPR_TYPE_AF_DISK: wait_for_dev(dev->gen_name); break; default: break; }; ipr_init_dev(dev); return 0; } /** * ipr_scan_ra - * @ioa: ipr ioa struct * @ra: ipr_res_addr struct * * Returns: * nothing **/ static void ipr_scan_ra(struct ipr_ioa *ioa, struct ipr_res_addr *ra) { ra_dbg(ra, "Scanning for new device\n"); ipr_scan(ioa, ra->bus, ra->target, ra->lun); } /** * ipr_for_each_unique_ra - * @dev: ipr dev struct * @func: function pointer * * Returns: * nothing **/ static void ipr_for_each_unique_ra(struct ipr_dev *dev, void (*func) (struct ipr_ioa *, struct ipr_res_addr *)) { signed int i, j; int dup; for (i = 0; i < ARRAY_SIZE(dev->res_addr); i++) { dup = 0; for (j = i - 1; j >= 0; j--) { if (!memcmp(&dev->res_addr[i], &dev->res_addr[j], sizeof(struct ipr_res_addr))) { dup = 1; break; } } if (!dup) func(dev->ioa, &dev->res_addr[i]); } } /** * fixup_improper_devs - * @ioa: ipr ioa struct * * Returns: * 0 if no improper devs / number of improper devs otherwise **/ static int fixup_improper_devs(struct ipr_ioa *ioa) { struct ipr_dev *dev; int improper = 0; for_each_dev(ioa, dev) { dev->local_flag = 0; if (ipr_improper_device_type(dev)) { improper++; dev->local_flag = 1; scsi_dbg(dev, "Deleting improper device\n"); ipr_write_dev_attr(dev, "delete", "1"); } } if (!improper) return 0; sleep(5); for_each_dev(ioa, dev) { if (!dev->local_flag) continue; dev->local_flag = 0; dev->rescan = 0; ipr_for_each_unique_ra(dev, ipr_scan_ra); } return improper; } /** * ipr_init_ioa - * @ioa: ipr ioa struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_init_ioa(struct ipr_ioa *ioa) { struct ipr_dev *dev; if (ioa->ioa_dead) return 0; if (fixup_improper_devs(ioa)) return -EAGAIN; for_each_dev(ioa, dev) ipr_init_dev(dev); init_ioa_dev(&ioa->ioa); return 0; } /** * scsi_dev_kevent - * @buf: data buffer * @find_device: function pointer * @func: function pointer * * Returns: * nothing **/ void scsi_dev_kevent(char *buf, struct ipr_dev *(*find_device)(char *), int (*func)(struct ipr_dev *)) { struct ipr_dev *dev; char *name; int i, j, rc = -EAGAIN; name = strrchr(buf, '/'); if (!name) { syslog_dbg("Failed to handle %s kevent\n", buf); return; } name++; for (i = 0; i < 2 && rc == -EAGAIN; i++) { for (j = 0; j < 10; j++) { tool_init(1); check_current_config(false); dev = find_device(name); if (dev) break; sleep(2); } if (!dev) { syslog_dbg("Failed to find ipr dev %s\n", name); return; } rc = func(dev); } } /** * scsi_host_kevent - * @buf: data buffer * @func: funtion pointer * * Returns: * nothing **/ void scsi_host_kevent(char *buf, int (*func)(struct ipr_ioa *)) { struct ipr_ioa *ioa; char *c; int host; int i, j, rc = -EAGAIN; c = strrchr(buf, '/'); if (!c) { syslog_dbg("Failed to handle %s kevent\n", buf); return; } c += strlen("/host"); host = strtoul(c, NULL, 10); for (i = 0; i < 2 && rc == -EAGAIN; i++) { for (j = 0; j < 10; j++) { tool_init(1); check_current_config(false); ioa = find_ioa(host); if (ioa) break; sleep(2); } if (!ioa) { syslog_dbg("Failed to find ipr ioa %d\n", host); return; } rc = func(ioa); } } /** * get_dev_from_addr - * @res_addr: ipr_res_addr struct * * Returns: * ipr_dev struct if success / NULL otherwise **/ struct ipr_dev *get_dev_from_addr(struct ipr_res_addr *res_addr) { struct ipr_ioa *ioa; int j; struct scsi_dev_data *scsi_dev_data; for_each_ioa(ioa) { for (j = 0; j < ioa->num_devices; j++) { scsi_dev_data = ioa->dev[j].scsi_dev_data; if (!scsi_dev_data) continue; if (scsi_dev_data->host == res_addr->host && scsi_dev_data->channel == res_addr->bus && scsi_dev_data->id == res_addr->target && scsi_dev_data->lun == res_addr->lun) return &ioa->dev[j]; } } return NULL; } /** * get_dev_from_handle - * @res_handle: * @ioa: struct ipr_ioa * * Returns: * ipr_dev struct if success / NULL otherwise **/ struct ipr_dev *get_dev_from_handle(struct ipr_ioa *ioa, u32 res_handle) { int j; for (j = 0; j < ioa->num_devices; j++) { if (!ioa->dev[j].qac_entry) continue; if (ipr_is_device_record(ioa->dev[j].qac_entry->record_id) && ioa->dev[j].resource_handle == res_handle) return &ioa->dev[j]; if (ipr_is_array_record(ioa->dev[j].qac_entry->record_id) && ioa->dev[j].resource_handle == res_handle) return &ioa->dev[j]; if (ipr_is_vset_record(ioa->dev[j].qac_entry->record_id) && ioa->dev[j].resource_handle == res_handle) return &ioa->dev[j]; } return NULL; } /** * ipr_daemonize - * * Returns: * nothing **/ void ipr_daemonize() { int rc = fork(); if (rc < 0) { syslog(LOG_ERR, "Failed to daemonize\n"); exit(1); } else if (rc) { exit(0); } close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); open(_PATH_DEVNULL,O_RDONLY); open(_PATH_DEVNULL,O_WRONLY); open(_PATH_DEVNULL,O_WRONLY); setsid(); } /** * ipr_disable_qerr - * @dev: ipr dev struct * * Returns: * 0 if success / non-zero on failure **/ int ipr_disable_qerr(struct ipr_dev *dev) { u8 ioctl_buffer[IOCTL_BUFFER_SIZE]; u8 ioctl_buffer2[IOCTL_BUFFER_SIZE]; struct ipr_control_mode_page *control_mode_page; struct ipr_control_mode_page *control_mode_page_changeable; struct ipr_mode_parm_hdr *mode_parm_hdr; int status; u8 length; /* Issue mode sense to get the control mode page */ status = ipr_mode_sense(dev, 0x0a, &ioctl_buffer); if (status) return -EIO; /* Issue mode sense to get the control mode page */ status = ipr_mode_sense(dev, 0x4a, &ioctl_buffer2); if (status) return -EIO; mode_parm_hdr = (struct ipr_mode_parm_hdr *)ioctl_buffer2; control_mode_page_changeable = (struct ipr_control_mode_page *) (((u8 *)(mode_parm_hdr+1)) + mode_parm_hdr->block_desc_len); mode_parm_hdr = (struct ipr_mode_parm_hdr *)ioctl_buffer; control_mode_page = (struct ipr_control_mode_page *) (((u8 *)(mode_parm_hdr+1)) + mode_parm_hdr->block_desc_len); /* Turn off QERR since some drives do not like QERR and IMMED bit at the same time. */ IPR_SET_MODE(control_mode_page_changeable->qerr, control_mode_page->qerr, 0); /* Issue mode select to set page x0A */ length = mode_parm_hdr->length + 1; mode_parm_hdr->length = 0; control_mode_page->hdr.parms_saveable = 0; mode_parm_hdr->medium_type = 0; mode_parm_hdr->device_spec_parms = 0; status = ipr_mode_select(dev, &ioctl_buffer, length); if (status) return -EIO; return 0; } void ipr_set_manage_start_stop(struct ipr_dev *dev) { char path[PATH_MAX]; ssize_t len; char value_str[2]; int value; sprintf(path, "/sys/class/scsi_disk/%s/device", dev->scsi_dev_data->sysfs_device_name); len = sysfs_read_attr(path, "manage_start_stop", value_str, 2); if (len < 0) { syslog_dbg("Failed to open manage_start_stop parameter.\n"); return; } value = atoi(value_str); snprintf(value_str, 2, "%d", 1); if (sysfs_write_attr(path, "manage_start_stop", value_str, 1) < 0) syslog_dbg("Failed to write manage_start_stop parameter.\n"); len = sysfs_read_attr(path, "manage_start_stop", value_str, 2); value = atoi(value_str); } /** * ipr_query_ioa_device_port - * @ioa: ipr ioa struct * @res_addr: ipr_res_addr struct * @option: option value * * Returns: * 0 if success / non-zero on failure **/ int ipr_query_io_dev_port(struct ipr_dev *dev, struct ipr_query_io_port *io_port) { int fd, rc, cdb_num; u8 cdb[IPR_CCB_CDB_LEN]; struct ipr_ioa *ioa = dev->ioa; struct sense_data_t sense_data; char *rp, *endptr; if (strlen(ioa->ioa.gen_name) == 0) return -ENOENT; fd = open(ioa->ioa.gen_name, O_RDWR); if (fd <= 1) { if (!strcmp(tool_name, "iprconfig") || ipr_debug) syslog(LOG_ERR, "Could not open %s. %m\n", ioa->ioa.gen_name); return errno; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_IOA_SERVICE_ACTION; cdb[1] = IPR_QUERY_IOA_DEV_PORT; if (ioa->sis64) { /* convert res path string to bytes */ cdb_num = 2; rp = dev->res_path_name; do { cdb[cdb_num++] = (u8)strtol(rp, &endptr, 16); rp = endptr+1; } while (*endptr != '\0' && cdb_num < 10); while (cdb_num < 10) cdb[cdb_num++] = 0xff; cdb[10] = sizeof(*io_port) >> 24; cdb[11] = (sizeof(*io_port) >> 16) & 0xff; cdb[12] = (sizeof(*io_port) >> 8) & 0xff; cdb[13] = sizeof(*io_port) & 0xff; } else { close(fd); return -1; } rc = sg_ioctl(fd, cdb, io_port, sizeof(*io_port), SG_DXFER_FROM_DEV, &sense_data, IPR_INTERNAL_TIMEOUT); if (rc != 0) scsi_cmd_err(dev, &sense_data, "Query IO port status", rc); close(fd); return rc; } iprutils/iprinit.c0000644000000000000000000000343712275251125013262 0ustar rootroot/** * IBM IPR adapter initialization utility * * (C) Copyright 2004, 2008 * International Business Machines Corporation and others. * All Rights Reserved. This program and the accompanying * materials are made available under the terms of the * Common Public License v1.0 which accompanies this distribution. * */ /* * $Header: /cvsroot/iprdd/iprutils/iprinit.c,v 1.22 2008/11/20 01:20:20 wboyer Exp $ */ #include #include #include #include #include #ifndef iprlib_h #include "iprlib.h" #endif #include char *tool_name = "iprinit"; static void init_all() { struct ipr_ioa *ioa; tool_init(1); check_current_config(false); for_each_ioa(ioa) ipr_init_ioa(ioa); } static void kevent_handler(char *buf) { polling_mode = 0; if (!strncmp(buf, "change@/class/scsi_host", 23) || (!strncmp(buf, "change@/devices/pci", 19) && strstr(buf, "scsi_host"))) scsi_host_kevent(buf, ipr_init_ioa); else if (!strncmp(buf, "add@/class/scsi_generic", 23) || (!strncmp(buf, "add@/devices/pci", 16) && strstr(buf, "scsi_generic"))) scsi_dev_kevent(buf, find_gen_dev, ipr_init_dev); else if (!strncmp(buf, "add@/block/sd", 13)) scsi_dev_kevent(buf, find_blk_dev, ipr_init_dev); } static void poll_ioas() { polling_mode = 1; init_all(); } int main(int argc, char *argv[]) { int i; ipr_sg_required = 1; openlog("iprinit", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); for (i = 1; i < argc; i++) { if (parse_option(argv[i])) continue; else { printf("Usage: iprinit [options]\n"); printf(" Options: --version [--daemon] Print iprinit version\n"); return -EINVAL; } } init_all(); if (daemonize) { ipr_daemonize(); return handle_events(poll_ioas, 60, kevent_handler); } return 0; } iprutils/iprdump.c0000644000000000000000000001525712275251164013272 0ustar rootroot/** * IBM IPR adapter dump daemon * * (C) Copyright 2000, 2004 * International Business Machines Corporation and others. * All Rights Reserved. This program and the accompanying * materials are made available under the terms of the * Common Public License v1.0 which accompanies this distribution. * */ /* * $Header: /cvsroot/iprdd/iprutils/iprdump.c,v 1.19 2008/11/20 01:20:20 wboyer Exp $ */ #ifndef iprlib_h #include "iprlib.h" #endif #include #define IPRDUMP_SIZE (80 * 1024 * 1024) #define MAX_DUMP_FILES 4 #define TOOL_NAME "iprdump" #define DUMP_PREFIX TOOL_NAME"." char *tool_name = TOOL_NAME; struct dump_header { u32 eye_catcher; #define IPR_DUMP_EYE_CATCHER 0xC5D4E3F2 u32 total_length; u32 num_elems; u32 first_entry_offset; u32 status; } *dump_header; struct dump_entry_header { u32 length; u32 id; #define IPR_DUMP_TEXT_ID 3 char data[0]; }; struct dump { struct dump_header hdr; u8 data[IPRDUMP_SIZE-sizeof(struct dump_header)]; }; static struct dump dump; static char usr_dir[100]; static char *enable = "1\n"; static char *disable = "0\n"; /** * enable_dump - * @ioa: ipr_ioa struct * * Returns: * nothing **/ static void enable_dump(struct ipr_ioa *ioa) { int rc; rc = ipr_write_host_attr(ioa, "dump", enable, strlen(enable)); if (rc != strlen(enable)) { ioa_err(ioa, "Failed to enable dump. rc=%d. %m\n", rc); return; } } /** * disable_dump - * @ioa: ipr_ioa struct * * Returns: * nothing **/ static void disable_dump(struct ipr_ioa *ioa) { int rc; rc = ipr_write_host_attr(ioa, "dump", disable, strlen(disable)); if (rc != strlen(disable)) { ioa_err(ioa, "Failed to disable dump. rc=%d. %m\n", rc); return; } } /** * read_dump - * @ioa: ipr_ioa struct * * Returns: * number of items read **/ static int read_dump(struct ipr_ioa *ioa) { int count = 0; count = ipr_read_host_attr(ioa, "dump", &dump, sizeof(dump)); return (count < 0) ? 0: count; } /** * select_dump_file - * @dirent: dirent struct * * Returns: * 1 if the string is found, else 0 **/ static int select_dump_file(const struct dirent *dirent) { if (strstr(dirent->d_name, DUMP_PREFIX)) return 1; return 0; } /** * dump_sort - * @a: void buffer * @b: void buffer * * Returns: * **/ static int dump_sort(const struct dirent **dumpa, const struct dirent **dumpb) { int numa, numb; sscanf((*dumpa)->d_name, DUMP_PREFIX"%d", &numa); sscanf((*dumpb)->d_name, DUMP_PREFIX"%d", &numb); if (numa < MAX_DUMP_FILES && numb >= (100 - MAX_DUMP_FILES)) return 1; if (numb < MAX_DUMP_FILES && numa >= (100 - MAX_DUMP_FILES)) return -1; return alphasort(dumpa, dumpb); } /** * cleanup_old_dumps - * * Returns: * Nothing **/ static void cleanup_old_dumps() { struct dirent **dirent; char fname[100]; int rc, i; rc = scandir(usr_dir, &dirent, select_dump_file, dump_sort); if (rc > 0) { for (i = 0 ; i < (rc - MAX_DUMP_FILES); i++) { sprintf(fname, "%s%s", usr_dir, dirent[i]->d_name); if (remove(fname)) { syslog(LOG_ERR, "Delete of %s%s failed. %m\n", usr_dir, dirent[i]->d_name); } } free(*dirent); } } /** * get_dump_fname - * @fname: file name * * Returns: * -EIO on error, else 0 **/ static int get_dump_fname(char *fname) { struct dirent **dirent; int rc; char *tmp; int dump_id = 0; cleanup_old_dumps(); rc = scandir(usr_dir, &dirent, select_dump_file, dump_sort); if (rc > 0) { rc--; tmp = strstr(dirent[rc]->d_name, DUMP_PREFIX); if (tmp) { tmp += strlen(DUMP_PREFIX); dump_id = strtoul(tmp, NULL, 10) + 1; if (dump_id > 99) dump_id = 0; } else { free(*dirent); fname[0] = '\0'; return -EIO; } free(*dirent); } sprintf(fname, "%s%02d", DUMP_PREFIX, dump_id); return 0; } /** * write_dump - * @ioa: ipr_ioa struct * @count: size to write * * Returns: * Nothing **/ static void write_dump(struct ipr_ioa *ioa, int count) { int f_dump; char dump_file[100], dump_path[100]; if (get_dump_fname(dump_file)) return; sprintf(dump_path, "%s%s", usr_dir, dump_file); f_dump = creat(dump_path, S_IRUSR); if (f_dump < 0) { syslog(LOG_ERR, "Cannot open %s. %m\n", dump_path); return; } write(f_dump, &dump, count); close(f_dump); ioa_err(ioa, "Dump of ipr IOA has completed to file: %s\n", dump_path); } /** * handle_signal - * @signal: signal value * * Returns: * Nothing **/ static void handle_signal(int signal) { struct ipr_ioa *ioa; tool_init(0); for_each_ioa(ioa) disable_dump(ioa); exit(0); } /** * select_dump_file - * @ioa: ipr_ioa struct * * Returns: * Nothing **/ static void dump_ioa(struct ipr_ioa *ioa) { int count; count = read_dump(ioa); if (count <= 0) return; write_dump(ioa, count); disable_dump(ioa); enable_dump(ioa); } /** * poll_for_dump - * * Returns: * Nothing **/ static void poll_for_dump() { struct ipr_ioa *ioa; tool_init(0); for_each_ioa(ioa) enable_dump(ioa); for_each_ioa(ioa) dump_ioa(ioa); } /** * kevent_handler - * @buf: character buffer * * Returns: * Nothing **/ static void kevent_handler(char *buf) { struct ipr_ioa *ioa; int host, i; char *index; if (!strncmp(buf, "change@/class/scsi_host", 23)) { host = strtoul(&buf[28], NULL, 10); } else if (!strncmp(buf, "change@/devices/pci", 19) && strstr(buf, "scsi_host")) { index = strrchr(buf, '/'); host = strtoul(index + 5, NULL, 10); } else return; for (i = 0; i < 10; i++) { tool_init(0); ioa = find_ioa(host); if (ioa) break; sleep(2); } if (!ioa) { syslog(LOG_ERR, "Failed to find ipr ioa %d for iprdump\n", host); return; } enable_dump(ioa); dump_ioa(ioa); } /** * main - * @argc: argument count * @argv: argument string * * Returns: * return value of handle_events() call **/ int main(int argc, char *argv[]) { struct ipr_ioa *ioa; int len, i; openlog(TOOL_NAME, LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); strcpy(usr_dir, IPRDUMP_DIR); for (i = 1; i < argc; i++) { if (parse_option(argv[i])) continue; if (strcmp(argv[i], "-d") == 0) { strcpy(usr_dir,argv[++i]); len = strlen(usr_dir); if (len < sizeof(usr_dir) && usr_dir[len] != '/') { usr_dir[len + 1] = '/'; usr_dir[len + 2] = '\0'; } } else { printf("Usage: "TOOL_NAME" [options]\n"); printf(" Options: --version Print iprdump version\n"); printf(" --daemon Run as a daemon\n"); printf(" --debug Print extra debugging information\n"); printf(" -d \n"); exit(1); } } if (daemonize) ipr_daemonize(); signal(SIGINT, handle_signal); signal(SIGQUIT, handle_signal); signal(SIGTERM, handle_signal); tool_init(0); for_each_ioa(ioa) enable_dump(ioa); return handle_events(poll_for_dump, 60, kevent_handler); } iprutils/iprdump.80000644000000000000000000000323012275251125013200 0ustar rootroot.\" (C) Copyright 2000, 2004 .\" International Business Machines Corporation and others. .\" All Rights Reserved. This program and the accompanying .\" materials are made available under the terms of the .\" Common Public License v1.0 which accompanies this distribution. .TH IPRDUMP 8 "February 2005" .SH NAME iprdump - IBM Power RAID adapter dump utility .SH SYNOPSIS .B "iprdump [-d directory]" .sp .BI "iprdump --version --debug --use-polling --use-uevents" .SH DESCRIPTION .B iprdump is used to gather information in the event of an adapter failure. The dump data will by default be saved in the /var/log/ directory with the prefix iprdump.# where # will be the dump ID of the file. The ipr dump utility will make a log in the system error log when a dump is taken. The iprdump utility can be started at any time and will properly handle adapters being dynamically added and removed from the system. .SH OPTIONS .TP .B \-\-version Print the version number of .B iprdump .TP .B \-\-debug Enable additional error logging. Enabling this will result in additional errors logging to /var/log/messages. .TP .B \-d Directory where dump data is to be stored. Default is /var/log/. .TP .B \-\-use-polling Do not use netlink/uevent notification, but rather poll for adapter and device configuration changes. .TP .B \-\-use-uevents Use netlink/uevent notification rather than polling for adapter and device configuration changes. If not specified, polling will be used until the first uevent notification appears, then netlink will be used. .SH AUTHOR Originally written by Michael Anderson . Rewritten for the 2.6 kernel by Brian King . iprutils/iprdbg.c0000644000000000000000000004320012275251125013043 0ustar rootroot/** * IBM IPR adapter debug utility * * (C) Copyright 2003 * International Business Machines Corporation and others. * All Rights Reserved. This program and the accompanying * materials are made available under the terms of the * Common Public License v1.0 which accompanies this distribution. * */ /* * $Header: /cvsroot/iprdd/iprutils/iprdbg.c,v 1.26 2006/09/08 16:26:01 brking Exp $ */ #ifndef iprlib_h #include "iprlib.h" #endif #include #include #include #include #include #include #include #include #define IPR_MAX_FLIT_ENTRIES 59 #define IPR_FLIT_TIMESTAMP_LEN 12 #define IPR_FLIT_FILENAME_LEN 184 #define IPRDBG_CONF "/etc/iprdbg.conf" #define IPRDBG_LOG "/var/log/iprdbg" #define SEPARATOR "--------------------------------------------------------------------------------" char *tool_name = "iprdbg"; enum iprdbg_cmd { IPRDBG_READ = IPR_IOA_DEBUG_READ_IOA_MEM, IPRDBG_WRITE = IPR_IOA_DEBUG_WRITE_IOA_MEM, IPRDBG_FLIT = IPR_IOA_DEBUG_READ_FLIT, IPRDBG_EDF = IPR_IOA_DEBUG_ENABLE_DBG_FUNC, IPRDBG_DDF = IPR_IOA_DEBUG_DISABLE_DBG_FUNC }; struct ipr_bus_speeds { u8 speed; char *description; }; struct ipr_bus_speeds bus_speeds[] = { {0, "Async"}, {1, "10MB/s"}, {2, "20MB/s"}, {3, "40MB/s"}, {4, "80MB/s"}, {5, "160MB/s (DT)"}, {13, "160MB/s (packetized)"}, {14, "320MB/s (packetized)"} }; struct ipr_flit_entry { u32 flags; u32 load_name; u32 text_ptr; u32 text_len; u32 data_ptr; u32 data_len; u32 bss_ptr; u32 bss_len; u32 prg_parms; u32 lid_flags; u8 timestamp[IPR_FLIT_TIMESTAMP_LEN]; u8 filename[IPR_FLIT_FILENAME_LEN]; }; struct ipr_flit { u32 ioa_ucode_img_rel_lvl; u32 num_entries; struct ipr_flit_entry flit_entry[IPR_MAX_FLIT_ENTRIES]; }; struct dbg_macro { char *cmd; }; static struct dbg_macro *macro; static int num_macros; static unsigned int last_adx; static unsigned int last_len; static unsigned int last_data; static char time_str[100]; static int getcurtime(char *buf, int max) { time_t cur_time, rc; struct tm *cur_tm; int len; buf[0] = '\0'; rc = time(&cur_time); if (rc == ((time_t)-1)) return -EIO; cur_tm = localtime(&cur_time); if (!cur_tm) return -EIO; len = strftime(buf, max, "%b %d %T", cur_tm); return len; } #define __logtofile(...) {if (outfile) {fprintf(outfile, __VA_ARGS__);}} #define logtofile(fmt, ...) \ do { \ if (outfile) { \ getcurtime(time_str, sizeof(time_str)); \ __logtofile("%s: "fmt, time_str, ##__VA_ARGS__); \ } \ } while (0) #define iprprint(...) {printf(__VA_ARGS__); logtofile(__VA_ARGS__);} #define __iprprint(...) {printf(__VA_ARGS__); __logtofile(__VA_ARGS__);} #define ipr_err(...) {fprintf(stderr, __VA_ARGS__); logtofile(__VA_ARGS__);} #define iprloginfo(...) {syslog(LOG_INFO, __VA_ARGS__); logtofile(__VA_ARGS__);} static FILE *outfile; static int debug_ioctl(struct ipr_ioa *ioa, enum iprdbg_cmd cmd, int ioa_adx, int mask, unsigned int *buffer, int len) { u8 cdb[IPR_CCB_CDB_LEN]; struct sense_data_t sense_data; int rc, fd; u32 direction = SG_DXFER_NONE; fd = open(ioa->ioa.gen_name, O_RDWR); if (fd < 0) { syslog(LOG_ERR, "Error opening %s. %m\n", ioa->ioa.gen_name); return fd; } memset(cdb, 0, IPR_CCB_CDB_LEN); cdb[0] = IPR_IOA_DEBUG; cdb[1] = cmd; cdb[2] = (ioa_adx >> 24) & 0xff; cdb[3] = (ioa_adx >> 16) & 0xff; cdb[4] = (ioa_adx >> 8) & 0xff; cdb[5] = ioa_adx & 0xff; cdb[6] = (mask >> 24) & 0xff; cdb[7] = (mask >> 16) & 0xff; cdb[8] = (mask >> 8) & 0xff; cdb[9] = mask & 0xff; cdb[10] = (len >> 24) & 0xff; cdb[11] = (len >> 16) & 0xff; cdb[12] = (len >> 8) & 0xff; cdb[13] = len & 0xff; if (cmd == IPRDBG_WRITE) direction = SG_DXFER_TO_DEV; else if (len > 0) direction = SG_DXFER_FROM_DEV; rc = sg_ioctl_noretry(fd, cdb, buffer, len, direction, &sense_data, IPR_INTERNAL_TIMEOUT); if (rc != 0) ipr_err("Debug IOCTL failed. %d\n", errno); close(fd); return rc; } static int format_flit(struct ipr_flit *flit) { int num_entries = ntohl(flit->num_entries) - 1; struct ipr_flit_entry *flit_entry; signed int len; char filename[IPR_FLIT_FILENAME_LEN+1]; char time[IPR_FLIT_TIMESTAMP_LEN+1]; char buf1[IPR_FLIT_FILENAME_LEN+1]; char buf2[IPR_FLIT_FILENAME_LEN+1]; char lid_name[IPR_FLIT_FILENAME_LEN+1]; char path[IPR_FLIT_FILENAME_LEN+1]; int num_args, i; for (i = 0, flit_entry = flit->flit_entry; (i < num_entries) && (*flit_entry->timestamp); flit_entry++, i++) { snprintf(time, IPR_FLIT_TIMESTAMP_LEN, "%s", (char *)flit_entry->timestamp); time[IPR_FLIT_TIMESTAMP_LEN] = '\0'; snprintf(filename, IPR_FLIT_FILENAME_LEN, "%s", (char *)flit_entry->filename); filename[IPR_FLIT_FILENAME_LEN] = '\0'; num_args = sscanf(filename, "%184s " "%184s " "%184s " "%184s ", buf1, buf2, lid_name, path); if (num_args != 4) { ipr_err("Cannot parse flit\n"); return 1; } len = strlen(path); iprprint("LID Name: %s (%8X)\n", lid_name, ntohl(flit_entry->load_name)); iprprint("LID Path: %s\n", path); iprprint("Time Stamp: %s\n", time); iprprint("LID Path: %s\n", lid_name); iprprint("Text Segment: Address %08X, Length %8X\n", ntohl(flit_entry->text_ptr), ntohl(flit_entry->text_len)); iprprint("Data Segment: Address %08X, Length %8X\n", ntohl(flit_entry->data_ptr), ntohl(flit_entry->data_len)); iprprint("BSS Segment: Address %08X, Length %8X\n", ntohl(flit_entry->bss_ptr), ntohl(flit_entry->bss_len)); __iprprint("%s\n", SEPARATOR); } return 0; } static void ipr_fgets(char *buf, int size, FILE *stream) { int i, ch = 0; for (i = 0; ch != EOF && i < (size - 1); i++) { ch = fgetc(stream); if (ch == '\n') break; buf[i] = ch; } buf[i] = '\0'; strcat(buf, "\n"); } static void dump_data(unsigned int adx, unsigned int *buf, int len) { char ascii_buffer[17]; int i, k, j; for (i = 0; i < (len / 4);) { iprprint("%08X: ", adx+(i*4)); memset(ascii_buffer, '\0', sizeof(ascii_buffer)); for (j = 0; i < (len / 4) && j < 4; j++, i++) { __iprprint("%08X ", ntohl(buf[i])); memcpy(&ascii_buffer[j*4], &buf[i], 4); for (k = 0; k < 4; k++) { if (!isprint(ascii_buffer[(j*4)+k])) ascii_buffer[(j*4)+k] = '.'; } } for (;j < 4; j++) { __iprprint(" "); strncat(ascii_buffer, "....", sizeof(ascii_buffer)-1); } __iprprint(" |%s|\n", ascii_buffer); } } static int flit(struct ipr_ioa *ioa, int argc, char *argv[]) { struct ipr_flit flit; int rc; rc = debug_ioctl(ioa, IPRDBG_FLIT, 0, 0, (unsigned int *)&flit, sizeof(flit)); if (!rc) format_flit(&flit); return rc; } static int speeds(struct ipr_ioa *ioa, int argc, char *argv[]) { unsigned int length = 64; unsigned int *buffer = calloc(length/4, 4); unsigned int adx, bus_speed; int num_buses, bus, i, j, rc; if (!buffer) { ipr_err("Memory allocation error\n"); return -ENOMEM; } if ((ioa->ccin == 0x2780) || (ioa->ccin == 0x2757)) num_buses = 4; else num_buses = 2; for (bus = 0, adx = 0xF0016380; bus < num_buses; bus++) { rc = debug_ioctl(ioa, IPRDBG_READ, adx, 0, buffer, length); if (!rc) { __iprprint("%s\n", SEPARATOR); iprprint("Bus %d speeds:\n", bus); for (i = 0; i < 16; i++) { bus_speed = ntohl(buffer[i]) & 0x0000000f; for (j = 0; j < sizeof(bus_speeds)/sizeof(struct ipr_bus_speeds); j++) { if (bus_speed == bus_speeds[j].speed) iprprint("Target %d: %s\n", i, bus_speeds[j].description); } } } switch(bus) { case 0: adx = 0xF001e380; break; case 1: adx = 0xF4016380; break; case 2: adx = 0xF401E380; break; }; } free(buffer); return 0; } static int eddf(struct ipr_ioa *ioa, enum iprdbg_cmd cmd, int argc, char *argv[]) { unsigned int parm1 = strtoul(argv[0], NULL, 16); unsigned int parm2 = 0; if (argc == 2) parm2 = strtoul(argv[1], NULL, 16); return debug_ioctl(ioa, cmd, parm1, parm2, NULL, 0); } static int edf(struct ipr_ioa *ioa, int argc, char *argv[]) { return eddf(ioa, IPRDBG_EDF, argc, argv); } static int ddf(struct ipr_ioa *ioa, int argc, char *argv[]) { return eddf(ioa, IPRDBG_DDF, argc, argv); } static int raw_cmd(struct ipr_ioa *ioa, int argc, char *argv[]) { int i, rc; unsigned int parm[4] = { 0 }; unsigned int *buf = NULL; for (i = 0; i < argc; i++) parm[i] = strtoul(argv[i], NULL, 16); if (argc == 4 && parm[3]) buf = calloc(parm[3]/4, 4); rc = debug_ioctl(ioa, parm[0], parm[1], parm[2], buf, parm[3]); if (!rc && buf) { dump_data(0, buf, parm[3]); last_data = ntohl(buf[0]); } free(buf); return rc; } static unsigned int get_adx(char *arg) { if (arg[0] == '@') { if (arg[1] == '+') return last_data + strtoul(&arg[2], NULL, 16); else if (arg[1] == '-') return last_data - strtoul(&arg[2], NULL, 16); else return last_data; } return strtoul(arg, NULL, 16); } static int bm4(struct ipr_ioa *ioa, int argc, char *argv[]) { unsigned int adx; unsigned int write_buf = htonl(strtoul(argv[1], NULL, 16)); unsigned int mask = strtoul(argv[2], NULL, 16); adx = get_adx(argv[0]); if ((adx % 4) != 0) { ipr_err("Address must be 4 byte aligned\n"); return -EINVAL; } return debug_ioctl(ioa, IPRDBG_WRITE, adx, mask, &write_buf, 4); } static int mw4(struct ipr_ioa *ioa, int argc, char *argv[]) { int i; unsigned int write_buf[16]; unsigned int adx; adx = get_adx(argv[0]); if ((adx % 4) != 0) { iprprint("Address must be 4 byte aligned\n"); return -EINVAL; } for (i = 0; i < (argc-1); i++) write_buf[i] = htonl(strtoul(argv[i+1], NULL, 16)); return debug_ioctl(ioa, IPRDBG_WRITE, adx, 0, write_buf, (argc-1)*4); } static int __mr4(struct ipr_ioa *ioa, unsigned int adx, int len) { unsigned int *buf; int rc; if ((adx % 4) != 0) { ipr_err("Address must be 4 byte aligned\n"); return -EINVAL; } if ((len % 4) != 0) { ipr_err("Length must be a 4 byte multiple\n"); return -EINVAL; } buf = calloc(len/4, 4); if (!buf) { ipr_err("Memory allocation error\n"); return -ENOMEM; } last_len = len; last_adx = adx; rc = debug_ioctl(ioa, IPRDBG_READ, adx, 0, buf, len); if (!rc) { dump_data(adx, buf, len); last_data = ntohl(buf[0]); } free(buf); return rc; } static int mr4(struct ipr_ioa *ioa, int argc, char *argv[]) { unsigned int adx; int len = 4; if (argc == 0) { if (!last_len) { ipr_err("Not enough parameters specified\n"); return -EINVAL; } adx = last_adx + last_len; len = last_len; } else { adx = get_adx(argv[0]); if (argc == 2) len = strtoul(argv[1], NULL, 16); } return __mr4(ioa, adx, len); } static int help(struct ipr_ioa *ioa, int argc, char *argv[]) { printf("\n"); printf("mr4 address length " "- Read memory\n"); printf("mw4 address data " "- Write memory\n"); printf("bm4 address data preserve_mask " "- Modify bits set to 0 in preserve_mask\n"); printf("edf parm1 parm2 " "- Enable debug function\n"); printf("ddf parm1 parm2 " "- Disable debug function\n"); printf("raw cmd [parm1] [parm2] [length] " "- Manually execute IOA debug command\n"); printf("speeds " "- Current bus speeds for each device\n"); printf("flit " "- Format the flit\n"); printf("macros " "- List currently loaded macros\n"); printf("exit " "- Exit the program\n\n"); return 0; } static int quit(struct ipr_ioa *ioa, int argc, char *argv[]) { closelog(); openlog("iprdbg", LOG_PID, LOG_USER); iprloginfo("iprdbg on %s exited\n", ioa->pci_address); closelog(); exit(0); return 0; } static int macros(struct ipr_ioa *ioa, int argc, char *argv[]) { int i; if (num_macros == 0) printf("No macros loaded. Place macros in "IPRDBG_CONF"\n"); for (i = 0; i < num_macros; i++) printf("%s\n", macro[i].cmd); return 0; } static const struct { char *cmd; int min_args; int max_args; int (*func)(struct ipr_ioa *,int argc, char *argv[]); } command [] = { { "mr4", 0, 2, mr4 }, { "mw4", 2, 17, mw4 }, { "bm4", 3, 3, bm4 }, { "edf", 1, 2, edf }, { "ddf", 1, 2, ddf }, { "raw", 1, 4, raw_cmd }, { "flit", 0, 0, flit }, { "speeds", 0, 0, speeds }, { "help", 0, 0, help }, { "?", 0, 0, help }, { "quit", 0, 0, quit }, { "q", 0, 0, quit }, { "exit", 0, 0, quit }, { "x", 0, 0, quit }, { "macros", 0, 0, macros }, }; static int last_cmd = -1; static int exec_cmd(struct ipr_ioa *ioa, int cmd, int argc, char *argv[]) { int num_args = argc - 1; if (num_args < command[cmd].min_args) { ipr_err("Not enough arguments specified.\n"); return -EINVAL; } if (num_args > command[cmd].max_args) { ipr_err("Too many arguments specified.\n"); return -EINVAL; } if (last_cmd != cmd) last_len = 0; last_cmd = cmd; return command[cmd].func(ioa, num_args, &argv[1]); } static int cmd_to_args(char *cmd, int *rargc, char ***rargv) { char *p; char **argv = NULL; int len = 0; int total_len = strlen(cmd); if (cmd[total_len-1] == '\n') { cmd[total_len-1] = '\0'; total_len--; } if (*cmd == '\0') return 0; for (p = strrchr(cmd, ' '); p; p = strrchr(cmd, ' ')) *p = '\0'; for (p = strrchr(cmd, '\t'); p; p = strrchr(cmd, '\t')) *p = '\0'; for (p = cmd, len = 0; p < cmd + total_len; p += (strlen(p) + 1), len++) { if (strlen(p) == 0) { len--; continue; } argv = realloc(argv, sizeof(char *)*(len+1)); argv[len] = p; } *rargv = argv; *rargc = len; return len; } static int exec_macro(struct ipr_ioa *ioa, char *cmd); static int __parse_cmd(struct ipr_ioa *ioa, int argc, char *argv[]) { int i; for (i = 0; i < ARRAY_SIZE(command); i++) { if (strcasecmp(argv[0], command[i].cmd) != 0) continue; return exec_cmd(ioa, i, argc, argv); } for (i = 0; i < num_macros; i++) { if (strncasecmp(argv[0], macro[i].cmd, strlen(argv[0])) != 0) continue; return exec_macro(ioa, macro[i].cmd); } iprprint("Invalid command %s. Use \"help\" for a list " "of valid commands\n", argv[0]); return -EINVAL; } static int parse_cmd(struct ipr_ioa *ioa, int argc, char *argv[]) { int i, rc = 0; int index = 0; char *end; for (i = 0; i < argc; i++) { end = strchr(argv[i], ':'); if (!end) end = strchr(argv[i], ';'); if (!end) continue; if (strlen(argv[i]) == 1) { rc = __parse_cmd(ioa, i - index, &argv[index]); } else { *end = '\0'; rc = __parse_cmd(ioa, (i + 1) - index, &argv[index]); } index = i + 1; if (rc) return rc; } return __parse_cmd(ioa, i - index, &argv[index]); } static int exec_macro(struct ipr_ioa *ioa, char *cmd) { int argc; char **argv = NULL; char *copy = malloc(strlen(cmd) + 1); int rc = -EINVAL; if (!copy) return -ENOMEM; strcpy(copy, cmd); if (!cmd_to_args(copy, &argc, &argv)) goto out_free_argv; rc = parse_cmd(ioa, argc - 1, &argv[1]); free(copy); out_free_argv: free(argv); return rc; } static int main_cmd_line(int argc, char *argv[]) { struct ipr_dev *dev; if (argc < 3) { ipr_err("Invalid option\n"); return -EINVAL; } dev = find_dev(argv[argc-1]); if (!dev) { ipr_err("Invalid IOA specified: %s\n", argv[argc-1]); return -EINVAL; } return parse_cmd(dev->ioa, argc-2, &argv[1]); } static struct ipr_ioa *select_ioa() { int i, num_args, ioa_num = 0; struct ipr_ioa *ioa; char cmd_line[1000]; if (num_ioas > 1) { printf("\nSelect adapter to debug:\n"); i = 1; for_each_ioa(ioa) printf("%d. IBM %X: Location: %s %s\n", i++, ioa->ccin, ioa->pci_address, ioa->ioa.gen_name); printf("%d to quit\n", i); printf("\nSelection: "); fgets(cmd_line, 999, stdin); num_args = sscanf(cmd_line, "%d\n", &ioa_num); if (ioa_num == i) return NULL; if (num_args != 1 || ioa_num > num_ioas) return NULL; for (ioa = ipr_ioa_head, i = 1; i < ioa_num; ioa = ioa->next, i++) {} } else ioa = ipr_ioa_head; return ioa; } static int exec_shell_cmd(struct ipr_ioa *ioa, char *cmd) { char **argv = NULL; int argc, rc; if (!cmd_to_args(cmd, &argc, &argv)) return mr4(ioa, 0, NULL); rc = parse_cmd(ioa, argc, argv); free(argv); return rc; } static void add_new_macro(char *cmd) { char *buf = malloc(strlen(cmd) + 1); if (!buf) return; macro = realloc(macro, (num_macros + 1) * sizeof(struct dbg_macro)); if (!macro) { free(buf); return; } strcpy(buf, cmd); if (buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = '\0'; macro[num_macros++].cmd = buf; } static void load_config() { char buf[2000]; FILE *config = fopen(IPRDBG_CONF, "r"); if (!config) return; while (!feof(config)) { if (!fgets(buf, sizeof(buf), config)) break; if (buf[0] == '#') continue; add_new_macro(buf); } fclose(config); } int main(int argc, char *argv[]) { char cmd_line[1000]; struct ipr_ioa *ioa; openlog("iprdbg", LOG_PERROR | /* Print error to stderr as well */ LOG_PID | /* Include the PID with each error */ LOG_CONS, /* Write to system console if there is an error sending to system logger */ LOG_USER); outfile = fopen(IPRDBG_LOG, "a"); if (!outfile) outfile = fopen(".iprdbglog", "a"); tool_init(0); check_current_config(false); load_config(); if (argc > 1) return main_cmd_line(argc, argv); ioa = select_ioa(); if (!ioa) return -ENXIO; closelog(); openlog("iprdbg", LOG_PID, LOG_USER); iprloginfo("iprdbg on %s started\n", ioa->pci_address); closelog(); openlog("iprdbg", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); printf("\nATTENTION: This utility is for adapter developers only! " "Use at your own risk!\n"); printf("\nUse \"quit\" to exit the program.\n\n"); while(1) { printf("IPRDB(%X)> ", ioa->ccin); ipr_fgets(cmd_line, 999, stdin); __logtofile("%s\n", SEPARATOR); logtofile("%s", cmd_line); exec_shell_cmd(ioa, cmd_line); } return 0; } iprutils/ipr.msg0000644000000000000000000005130412275251125012736 0ustar rootroot$quote " $set 0 Generic 1 "Select one of the following" 2 "Selection" 3 "Complete" 4 "to return to menu, current operations will continue" $set 101 main_menu 1 "Work with Disk Units" 2 "Display disk hardware status" 3 "Work with Disk Arrays" 4 "Work with disk unit recovery" 5 "Work with SCSI bus configuration" 6 "Work with driver configuration" 7 "Analyze log" $set 102 disk_status 1 "Display Disk Hardware Status" 2 "Type option, press Enter." 3 "Display hardware resource information details" 4 "No devices found" $set 104 device_details 1 "IOA Hardware Resource Information Details" 2 "Manufacturer" 3 "Machine Type and Model" 4 "Driver Version" 5 "Firmware Version" 6 "Serial Number" 7 "Part Number" 8 "Plant of Manufacturer" 9 "Cache Size" 10 "DRAM Size" 11 "Resource Name" 12 "Physical location" 13 "PCI Address" 14 "SCSI Host Number" 15 "Resource Path" 101 "Disk Array Information Details" 102 "RAID Level" 103 "Stripe Size" 104 "Capacity" 201 "Disk Unit Hardware Resource Information Details" 202 "Product ID" 203 "SCSI Host Number" 204 "SCSI Channel" 205 "SCSI Id" 206 "SCSI Lun" 207 "Level" 208 "Extended Details" 209 "FRU Number" 210 "EC Level" 211 "Device Specific (Z0)" 212 "Device Specific (Z1)" 213 "Device Specific (Z2)" 214 "Device Specific (Z3)" 215 "Device Specific (Z4)" 216 "Device Specific (Z5)" 217 "Device Specific (Z6)" $set 200 raid_screen 1 "Work with Disk Arrays" 2 "Display disk array status" 3 "Create a disk array" 4 "Delete a disk array" 5 "Add a device to a disk array" 6 "Format device for advanced function" 7 "Format device for JBOD function" 8 "Configure a hot spare device" 9 "Unconfigure a hot spare device" $set 201 raid_status 1 "Display Disk Array Status" 2 "Type option, press Enter." 3 "Display hardware resource information details" 4 "No devices found" $set 202 raid_start 1 "Create a Disk Array" 2 "Select the subsystems to create a disk array." 3 "Type choice, press Enter." 4 "create a disk array" $set 203 raid_stop 1 "Delete a Disk Array" 2 "Select the subsystems to delete a disk array." 3 "Type choice, press Enter." 4 "delete a disk array" $set 204 raid_stop_fail 1 "Delete a Disk Array Failed" 2 "There are no disk units eligible for the selected operation\ due to one or more of the following reasons:" 3 "There are no device parity protected units in the system." 4 "An IOA is in a condition that makes the disk units attached to\ it read/write protected. Examine the kernel messages log\ for any errors that are logged for the IO subsystem\ and follow the appropriate procedure for the reference code to\ correct the problem, if necessary." 5 "Not all disk units attached to an advanced function IOA have\ reported to the system. Retry the operation." 6 "The disk units are missing." 7 "The disk unit/s are currently in use." $set 205 confirm_raid_stop 1 "Confirm Delete a Disk Array" 2 "ATTENTION: Disk units connected to these subsystems will not be\ protected after you confirm your choice." 3 "Press Enter to continue." 4 "Cancel to return and change your choice." $set 207 raid_start_fail 1 "Create a Disk Array Failed" 2 "There are no disk units eligible for the selected operation due\ to one or more of the following reasons:" 3 "There are not enough device parity capable units in the system." 4 "An IOA is in a condition that makes the disk units attached to\ it read/write protected. Examine the kernel messages log\ for any errors that are logged for the IO subsystem\ and follow the appropriate procedure for the reference code to\ correct the problem, if necessary." 5 "Not all disk units attached to an advanced function IOA have\ reported to the system. Retry the operation." 6 "The disk units are missing." 7 "The disk unit/s are currently in use." $set 208 configure_raid_start 1 "Select Disk Units for Parity Protection" 2 "Type option, press Enter." 3 "Select" $set 210 confirm_raid_start 1 "Confirm Start Device Parity Protection" 2 "ATTENTION: Data will not be preserved and may be lost on selected disk\ units when parity protection is enabled." 3 "Press Enter to continue." 4 "Cancel to return and change your choice." $set 211 raid_start_complete 1 "Start Device Parity Protection Status" 2 "You selected to start device parity protection" $set 215 raid_include 1 "Add a Device to a Disk Array" 2 "Select the subsystem that the disk unit will be included." 3 "Type choice, press Enter." 4 "Select Parity Array to Include Disk Unit" $set 216 raid_include_fail 1 "Include Device Parity Protection Failed" 2 "There are no disk units eligible for the selected operation\ due to one or more of the following reasons:" 3 "There are no device parity protected units in the system." 4 "An IOA is in a condition that makes the disk units attached to\ it read/write protected. Examine the kernel messages log\ for any errors that are logged for the IO subsystem\ and follow the appropriate procedure for the reference code to\ correct the problem, if necessary." 5 "Not all disk units attached to an advanced function IOA have\ reported to the system. Retry the operation." 6 "The disk units are missing." 7 "The disk unit/s are currently in use." $set 217 configure_raid_include 1 "Include Disk Units in Device Parity Protection" 2 "Select the units to be included in Device Parity Protection" 3 "Type choice, press Enter." 4 "Add a Device to a Disk Array" $set 218 configure_raid_include_fail 1 "Include Disk Unit Failed" 2 "There are no disk units eligible for the selected operation\ due to one or more of the following reasons:" 3 "There are not enough disk units available to be included." 4 "The disk unit that needs to be included is not at the right\ location. Examine the 'message log' for the exposed unit\ and make sure that the replacement unit is at the correct\ location" 5 "Not all disk units attached to an IOA have reported to the\ system. Retry the operation." 6 "The disk unit to be included must be the same or greater\ capacity than the smallest device in the volume set and\ be formatted correctly" 7 "The type/model of the disk units is not supported for the\ requested operation" $set 219 confirm_raid_include 1 "Confirm Disk Units to be Included" 2 "ATTENTION: All listed device units will be formatted and zeroed before\ listed device is added to array." 3 "Press Enter to confirm your choice to have the system include\ the selected units in device parity protection" 4 "Cancel to return and change your choice." $set 221 dev_include_complete 1 "Include Disk Units in Device Parity Protection Status" 2 "The operation to include units in the device parity protection\ will be done in two phases. The device must first be formatted\ before device may be included into array set." 3 "You selected to include a device in a device parity set" $set 230 af_include_fail 1 "Format Device for Advanced Function Failed" 2 "There are no disk units eligible for the selected operation\ due to one or more of the following reasons:" 3 "There are no eligible device units in the system." 4 "An IOA is in a condition that makes the disk units attached to\ it read/write protected. Examine the kernel messages log\ for any errors that are logged for the IO subsystem\ and follow the appropriate procedure for the reference code to\ correct the problem, if necessary." 5 "Not all disk units attached to an advanced function IOA have\ reported to the system. Retry the operation." 6 "The disk units are missing." 7 "The disk unit/s are currently in use." $set 231 af_remove_fail 1 "Disable Device for Advanced Functions Failed" 2 "There are no disk units eligible for the selected operation\ due to one or more of the following reasons:" 3 "There are no eligible device units in the system." 4 "An IOA is in a condition that makes the disk units attached to\ it read/write protected. Examine the kernel messages log\ for any errors that are logged for the IO subsystem\ and follow the appropriate procedure for the reference code to\ correct the problem, if necessary." 5 "Not all disk units attached to an advanced function IOA have\ reported to the system. Retry the operation." 6 "The disk units are missing." 7 "The disk unit/s are currently in use." $set 232 add_hot_spare_fail 1 "Enable Device as Hot Spare Failed" 2 "There are no disk units eligible for the selected operation\ due to one or more of the following reasons:" 3 "There are no eligible device units in the system." 4 "An IOA is in a condition that makes the disk units attached to\ it read/write protected. Examine the kernel messages log\ for any errors that are logged for the IO subsystem\ and follow the appropriate procedure for the reference code to\ correct the problem, if necessary." 5 "Not all disk units attached to an advanced function IOA have\ reported to the system. Retry the operation." 6 "The disk units are missing." 7 "The disk unit/s are currently in use." $set 233 remove_hot_spare_fail 1 "Disable Device as Hot Spare Failed" 2 "There are no disk units eligible for the selected operation\ due to one or more of the following reasons:" 3 "There are no eligible device units in the system." 4 "An IOA is in a condition that makes the disk units attached to\ it read/write protected. Examine the kernel messages log\ for any errors that are logged for the IO subsystem\ and follow the appropriate procedure for the reference code to\ correct the problem, if necessary." 5 "Not all disk units attached to an advanced function IOA have\ reported to the system. Retry the operation." 6 "The disk units are missing." 7 "The disk unit/s are currently in use." $set 236 1 "Configure a hot spare device" 2 "Select the subsystems which disk units will be configured as hot spares" 3 "Type choice, press Enter." 4 "Select subsystem" 5 "Unconfigure a hot spare device" 6 "Select the subsystems which disk units will be unconfigured as hot spares" 7 "Type choice, press Enter." 8 "Select subsystem" $set 240 select_hot_spare 1 "Select Disk Units to Enable as Hot Spare" 2 "Type option, press Enter." 3 "Select" 4 "Select Disk Units to Disable as Hot Spare" 5 "Type option, press Enter." 6 "Select" $set 245 confirm_hot_spare 1 "Confirm Enable Hot Spare" 2 "ATTENTION: Existing data on these disk units will not be preserved." 3 "Press Enter to continue." 4 "Cancel to return and change your choice." 5 "Confirm Disable Hot Spare" 6 "ATTENTION: Existing data on these disk units will not be preserved." 7 "Press Enter to continue." 8 "Cancel to return and change your choice." $set 300 disk_unit_recovery 1 "Work with Disk Unit Recovery" 2 "Concurrent Add Device" 3 "Concurrent Remove/Replace Device" 4 "Initialize and format disk unit" 5 "Reclaim IOA cache storage" 6 "Rebuild disk unit data" 7 "Work with resources containing cache battery packs" $set 301 concurrent_add_device 1 "Concurrent Device Add" 2 "Choose a single location for add operations" 3 "Select" $set 302 concurrent_remove_device 1 "Concurrent Device Remove" 2 "Choose a single location for remove operations" 3 "Select" 4 "Concurrent Device Add" 5 "Choose a single location for add operations" 6 "Select" $set 303 process_conc_maint 1 "Verify Device Concurrent Remove" 2 "Verify the selected device for concurrent remove operations" 3 "Press Enter when verification complete and ready to continue" 4 "Cancel to return and change your choice." 5 "Verify Device Concurrent Add" 6 "Verify the seleted device for concurrent add operations" 7 "Press Enter when verification complete and ready to continue" 8 "Cancel to return and change your choice." 9 "Complete Device Concurrent Remove" 10 "Remove selected device" 11 "Press Enter when selected device has been removed" 12 "Cancel to return and change your choice." 13 "Complete Device Concurrent Add" 14 "Add device to selected location" 15 "Press Enter when device has been installed" 16 "Cancel to return and change your choice." $set 312 configure_af_device | init_device 1 "Select Disk Units to format for Advanced Function" 2 "Select Disk Units to format for JBOD Function" 3 "Select Disk Units for Initialize and Format" 4 "Type option, press Enter." 5 "Select" $set 313 1 "Confirm Initialize and Format Disk Unit" 2 "Press 'c' to confirm your choice for 1=Initialize and format." 3 "Return to change your choice." $set 314 dev_init_complete 1 "Initialize and Format Status" 2 "You selected to initialize and format a disk unit" 4 "Please wait for the next screen." $set 315 reclaim_cache 1 "Reclaim IOA Cache Storage" 2 "Select the IOA to reclaim IOA cache storage." 3 "ATTENTION: Proceed with this function only if directed to from a\ service procedure. Data in the IOA cache will be discarded.\ Damaged objects may result on the system." 4 "Type choice, press Enter." 5 "Reclaim IOA cache storage." $set 316 confirm_reclaim 1 "Confirm Reclaim IOA Cache Storage" 2 "The disk units that may be affected by the function are displayed." 3 "ATTENTION: Proceed with this function only if directed to from a\ service procedure. Data in the IOA cache will be discarded.\ Filesystem corruption may result on the system." 4 "Press c=Confirm to reclaim cache storage." 5 "Cancel to return to change your choice." $set 317 confirm_reclaim_warning 2 "ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!!" 3 "ATTENTION: Proceed with this function only if directed to from a\ service procedure. Data in the IOA cache will be discarded.\ By proceeding you will be allowing UNKNOWN data loss.\ This data loss may or may not be detected by the host operating system.\ Filesystem corruption may result on the system." 4 "Press 's' to continue." $set 319 reclaim_result 1 "Reclaim IOA Cache Storage Results" 2 "IOA cache storage reclamation has completed. Use the number\ of lost sectors to decide whether to restore data from the\ most recent save media or to continue with possible data loss." 3 "Number of lost sectors" 4 "IOA cache storage reclamation has completed. The number of\ lost sectors could not be determined." $set 321 raid_rebuild 1 "Rebuild Disk Unit Data" 2 "Select the units to be rebuilt" 3 "Type choice, press Enter." 4 "Rebuild" $set 322 raid_rebuild_fail 1 "Rebuild Disk Unit Data Failed" 2 "There are no disk units eligible for the selected operation\ due to one or more of the following reasons:" 3 "There are no disk units that need to be rebuilt." 4 "The disk unit that needs to be rebuild is not at the right\ location. Examine the 'message log' for the exposed unit\ and make sure that the replacement unit is at the correct\ location." 5 "Not all disk units attached to an IOA have reported to the\ system. Retry the operation." 6 "All the disk units in a parity set must be the same capacity\ with a minumum number of 4 disk units and a maximum of 10 units\ in the resulting parity set." 7 "The type/model of the disk units is not supported for the\ requested operation." $set 323 confirm_raid_rebuild 1 "Confirm Rebuild Disk Unit Data" 2 "Rebuilding the disk unit data may take several minutes for\ each unit selected." 3 "Press Enter to confirm having the data rebuilt." 4 "Cancel to return and change your choice." $set 330 battery_maint 1 "Work with Resources Containing Cache Battery Packs" 2 "Type options, press Enter" 3 "Force battery pack into error state" 4 "Display battery information" $set 341 confirm_force_battery_error 1 "Force Battery Packs Into Error State" 2 "ATTENTION: This service function should be run only under the direction\ of the IBM Hardware Service Support" 3 "You have selected to force a cache batter error on an IOA" 4 "You will have to replace the Cache Battery Pack in each selected IOA to\ resume normal operations." 5 "System performance could be significantly degraded until the cache battery\ packs are replaced on the selected IOAs." 6 "Continue to force the following battery packs into an error state" $set 332 show_battery_info 1 "Battery Information" 2 "Resource Name" 3 "Serial Number" 4 "Type" 5 "PCI Address" 6 "SCSI Host Number" 7 "Battery type" 8 "Battery state" 9 "Power on time (days)" 10 "Adjusted power on time (days)" 11 "Estimated time to warning (days)" 12 "Estimated time to error (days)" 13 "Resource Path" 21 "Nickel Cadmium (NiCd)" 22 "Nickel Metal Hydride (NiMH)" 23 "Lithium Ion (LiIon)" 24 "Unknown" 31 "No battery warning" 32 "Battery warning issued" 33 "Battery error issued" $set 401 bus_config 1 "Work with SCSI Bus Configuration" 2 "Select the subsystem to change scsi bus attribute." 3 "Type choice, press Enter." 4 "change scsi bus attribute" $set 402 bus_config_fail 1 "Work with SCSI Bus Configuration Failed" 2 "There are no SCSI Buses eligible for the selected operation.\ None of the installed adapters support the requested\ operation." $set 403 change_bus_attr 1 "Change SCSI Bus Configuration" 2 "Current Bus configurations are shown. To change\ setting hit 'c' for options menu. Hightlight desired\ option then hit Enter" 3 "Change Setting" 4 "BUS" 5 "QAS Capability" 6 "Host SCSI ID" 7 "Wide Enabled" 8 "Maximum Bus Throughput" 9 "Enable" 10 "Enabled" 11 "Disable" 12 "Disabled" 13 "Yes" 14 "No" 21 "Confirm Change SCSI Bus Configuration" 22 "Confirming this action will result in the following\ configuration to become active." 23 "Confirm Change SCSI Bus Configration" $set 458 driver_config 1 "Adapter Driver Configuration" 2 "Select subsystem to change driver configuration" 3 "Type choice, press Enter" 4 "change driver configuration" $set 459 change_driver_config 1 "Change Driver Configuration" 2 "Current Driver configurations are shown. To change\ setting, type in new value then hit Enter" 3 "Current Verbosity" 4 "New Verbosity" $set 500 log_menu 1 "Kernel Messages Log" 2 "View most recent IBM Storage error messages" 3 "View IBM Storage error messages" 4 "View all kernel error messages" 5 "View sisconfig error messages" 6 "Set root kernel message log directory" 7 "Set default editor" 8 "Restore defaults" 9 "View IBM boot time messages" $set 501 kernel_root 1 "Kernel Messages Log Root Directory" 2 "Enter new directory and press Enter" 3 "Current root directory:" 4 "New root directory:" $set 502 confirm_kernel_root 1 "Confirm Root Directory Change" 2 "Press 'c' to confirm your new kernel root directory choice." 3 "To return to the log menu without changing the root directory, choose 'q'." 4 "New root directory:" $set 503 set_default_editor 1 "Kernel Messages Editor" 2 "Enter new editor command and press Enter" 3 "Current editor:" 4 "New editor:" $set 504 confirm_set_default_editor 1 "Confirm Editor Change" 2 "Press 'c' to confirm your editor choice" 3 "To return to the log menu without changing the editor, choose 'q'." 4 "New editor:" $set 888 screen status strings 1 "" 2 "Invalid option specified" 3 "Screen paged up" 4 "Already at top" 5 "Screen paged down" 6 "Already at bottom" 10 "No devices to show details for" 15 "The selection is not valid" 16 "Error. More than one unit was selected." 17 "Invalid option. No devices selected." 18 "Start Parity Protection completed successfully" 19 "Start Parity Protection failed." 20 "Stop Parity Protection failed" 21 "Stop Parity Protection completed successfully." 25 "Error: number of devices selected must be a multiple of %d" 26 "Include failed" 27 "Include Unit completed successfully" 28 "Rebuild started, view Parity Status Window for rebuild progress" 29 "Rebuild failed" 30 "Device concurrent maintenance failed" 31 "Device concurrent maintenance failed, device in use" 32 "Device concurrent maintenance completed successfully" 33 "No units available for initialize and format" 34 "Initialize and format completed successfully" 35 "Initialize and format failed" 36 "Reclaim IOA Cache Storage completed successfully" 37 "Reclaim IOA Cache Storage failed" 38 "No Reclaim IOA Cache Storage is necessary" 39 "No Reclaim IOA Cache Storage performed" 40 "Rebuild started, view Parity Status Window for rebuild progress" 41 "Rebuild failed" 42 "The selected battery packs have successfully been placed into an error state." 43 "Attempting to force the selected battery packs into an error state failed." 44 "No configured resources contain a cache battery pack." 45 "Change SCSI Bus configurations completed successfully" 46 "Change SCSI Bus configurations failed" 47 "Change Device Driver Configurations completed successfully" 48 "Change Device Driver Configurations failed" 49 "No units available for initialize and format" 50 "Initialize and format completed successfully" 51 "Initialize and format failed" 52 "No devices available for the selected hot spare operation" 53 "Enable Device as Hot Spare Completed successfully" 54 "Disable Device as Hot Spare Completed successfully" 55 "Configure a hot spare device failed" 56 "Unconfigure a hot spare device failed" 57 "Change Device Driver Configurations completed successfully" 58 "Change Device Driver Configurations failed" 59 "Invalid directory" 60 "Root directory changed to %s" 61 "Root directory unchanged" 62 "Editor changed to %s" 63 "Editor unchanged" 64 "Default log values restored" 65 "Editor returned %d. Try setting the default editor." $set 999 empty set used for screens without displays iprutils/version.mk0000644000000000000000000000131112275251164013446 0ustar rootroot# (C) Copyright 2000, 2011 # International Business Machines Corporation and others. # All Rights Reserved. This program and the accompanying # materials are made available under the terms of the # Common Public License v1.0 which accompanies this distribution. IPR_MAJOR_RELEASE=2 IPR_MINOR_RELEASE=4 IPR_FIX_LEVEL=0 IPR_RELEASE=1 IPR_FIX_DATE=(Feb 07, 2014) IPR_VERSION_STR=$(IPR_MAJOR_RELEASE).$(IPR_MINOR_RELEASE).$(IPR_FIX_LEVEL) $(IPR_FIX_DATE) IPR_DEFINES = -DIPR_MAJOR_RELEASE=$(IPR_MAJOR_RELEASE) \ -DIPR_MINOR_RELEASE=$(IPR_MINOR_RELEASE) \ -DIPR_FIX_LEVEL=$(IPR_FIX_LEVEL) \ -DIPR_FIX_DATE='"$(IPR_FIX_DATE)"' \ -DIPR_VERSION_STR='"$(IPR_VERSION_STR)"' \ -DIPR_RELEASE=$(IPR_RELEASE) iprutils/iprconfig.80000644000000000000000000006251112275251125013507 0ustar rootroot.\" (C) Copyright 2000, 2009 .\" International Business Machines Corporation and others. .\" All Rights Reserved. This program and the accompanying .\" materials are made available under the terms of the .\" Common Public License v1.0 which accompanies this distribution. .TH IPRCONFIG 8 "August 2009" .SH NAME iprconfig \- IBM Power RAID storage adapter configuration/recovery utility .SH SYNOPSIS .BI "iprconfig [-e editor] [-k dir] [-c command]" .sp .BI "iprconfig --version --debug --force" .SH DESCRIPTION .B iprconfig is used to configure IBM Power RAID storage adapters, display information about them, and to perform adapter and disk unit recovery. The menu options are: .PP .B 1. Display hardware status. .br This option can be used to display various information regarding the IBM Power RAID adapters attached to the system and the disk units controlled by them. For each adapter and disk unit, their /dev name, physical location, description, vendor/product ID, and hardware status will be available. Beside each resource is an OPT field. By entering a 1 beside any resource, detailed information about that resource can be obtained. For an adapter resource, this will display the adapter firmware version and the physical location amongst other things. .PP .B 2. Work with Disk Arrays .br This option will present a second menu containing disk array related commands. .PP .B Display disk array status is used to display the status of disk arrays on the system. .PP .B Create a disk array is used to create a disk array. .PP .B Delete a disk array is used to delete disk arrays. Selecting this option will provide you with a list of disk arrays which can be deleted. .PP .B Add a device to a disk array is used to include devices of similar capacity into an existing disk array. This function is currently only supported for RAID 5 and RAID 6 disk arrays. .PP .B Format device for advanced function is used to format disks to 522 bytes/sector so that they may be used in a disk array. Only disks which are not formatted for advanced function or are formatted for advanced function but are not known to be zeroed will be available for selection for this function. .PP .B Format device for JBOD function (512) is used to format disks to 512 bytes/sector so that they may be used as standalone disks. Only disks which are not formatted for JBOD function or are formatted for JBOD function and are in the Format Required state will be available for this function. .PP .B Work with hot spares is used to create a hot spare which designates a device as a dedicated hot spare. It is also used to delete a hot spare which unconfigures a previously configured hot spare. .\".B Create a hot spare .\"is used to designate a device as a dedicated hot spare. .\".PP .\".B Delete a hot spare .\"is used to unconfigure a previously configured hot spare. .PP .B Work with asymmetric access is used to select which path of a disk array will be the primary path in a dual controller environment. Asymmetric Access must be enabled on the adapter first. Not all adapters support asymmetric access and adapters that do provide support may require updated microcode. .PP .B Force RAID Consistency Check is used to force a consistency check on a RAID array. All ipr adapters continually perform background consistency checking when idle. This option can be used to force a consistency check to be performed. .PP .B Migrate disk array protection is used to change the RAID protection level for an array to another supported level. In some cases, this will require adding more disks to the array. In other cases, disks will be freed. .PP .B 3. Work with disk unit recovery is used to perform the following disk unit recovery actions: .PP .B Concurrent add device is used to concurrently add a new disk to a running system. This feature is only supported with SES (SCSI Enclosure Services) packaging. .PP .B Concurrent remove device is used to concurrently remove a disk from a running system. This feature is only supported with SES (SCSI Enclosure Services) packaging. .PP .B Initialize and format disk unit is used to issue a SCSI format command to attached devices. A format unit command has special meaning to the adapter and is used as a service action for certain error conditions. Formatting a disk unit will lose all data on that drive. If the disk is attached to an ipr adapter that does not support RAID, the drive will be formatted to 512 bytes/sector. If the disk is attached to an ipr RAID adapter, the block size will not be changed. To change the block size, use the format menu options under the disk arrays menu. .PP .B Reclaim IOA cache storage is used to repair cache error conditions. .B ATTENTION: Use this option with care. This is used to discard data from the cache and may result in data loss. This option is designed to be used by authorized IBM hardware customer engineers. .PP .B Rebuild disk unit data is generally used following concurrent maintenance. Select this option after a failing array member device has been replaced to reconstruct the device as an active array member. .PP .B Work with resources containing cache battery packs is used to display information regarding rechargeable cache battery packs and to force rechargeable cache battery packs into an error state so that they can be replaced prior to failure. .B ATTENTION: Once an error has been forced on a rechargeable cache battery pack write caching will be disabled until the battery pack is replaced. .PP .B 4. Work with SCSI bus configuration is used to change configurable SCSI bus attributes, such as maximum SCSI bus speed, SCSI initiator ID, etc. .PP .B 5. Work with driver configuration is used to change driver configurable attributes, such as log_level. .PP .B 6. Work with disk configuration is used to change configurable disk attributes, such as queue depth. .PP .B 7. Work with adapter configuration is used to change configurable adapter attributes, such as dual adapter settings. Refer to the following command line options: primary, secondary, query-ha-mode, set-ha-mode, set-ioa-asymmetric-access and set-array-asymmetric-access for more information regarding these settings. .PP .B 8. Download microcode is used to download microcode to ipr adapters and attached SCSI disks. .PP .B 9. Analyze Log is an option available to analyze /var/log/messages* files. By default it will use vi as the editor to open the concatenated error log files. This can be changed by using option 6 on the .B Kernel Messages Log menu. Selecting option 1 on the Kernel Messages Log menu will display only the most recent errors logged by the ipr device driver and may be useful to filter out some of the clutter. Option 2 will display all recorded errors logged by the ipr device driver. Option 3 will display all kernel messages. Option 4 will display errors logged by the iprconfig utility. This may be useful for debugging problems. Option 5 can be used to change where the tool looks to find the kernel messages files. The default is to look in /var/log. .SH OPTIONS .TP .B \-e editor Default editor for viewing error logs. The default editor is vi, but can be changed with this parameter. .TP .B -k directory Kernel messages root directory. Root directory to look for kernel messages. Default is /var/log. .TP .B -c command Command line, non-interactive commands. Currently supported commands include: .RS .TP .B show-config .br Show ipr configuration. .TP .B show-alt-config .br Show alternate ipr configuration information. .TP .B show-ioas .br Show all ipr adapters. .TP .B show-arrays .br Show all ipr arrays. .TP .B show-battery-info [IOA] .br Show cache battery information for specified IOA. Example: .br .B iprconfig -c show-battery-info sg5 .TP .B show-details [device] .br Show device details for specified device. Example: .br .B iprconfig -c show-details sda .TP .B show-hot-spares .br Show all configured hot spares. .TP .B show-af-disks .br Show disks formatted for Advanced Function that are not configured in an array or as a hot spare. .TP .B show-all-af-disks .br Show all disks formatted for Advanced Function .TP .B show-jbod-disks .br Show all disks formatted for JBOD Function. .TP .B status [device] .br Show the status of the specified device. This is the same status as which shows up in the last column of the Display hardware status menu. Can specify either a /dev/sdX name or a /dev/sgX name. Example: .br .B iprconfig -c status /dev/sda .br .TP .B alt-status [device] .br Show the status of the specified device. This is the same status as above with the exception of when a long running command is executing to the device, in which case the percent complete for the long running command is printed. .br .TP .B query-raid-create [IOA] .br Show all devices attached to the specified IOA that are candidates for being used in a RAID array. Example: .br .B iprconfig -c query-raid-create sg5 .TP .B query-raid-delete [IOA] .br Show all RAID arrays attached to the specified IOA that can be deleted. .br .B iprconfig -c query-raid-delete sg5 .TP .B query-hot-spare-create [IOA] .br Show all devices attached to the specified IOA that are candidates for being hot spares. .TP .B query-hot-spare-delete [IOA] .br Show all hot spares attached to the specified IOA that can be deleted. .TP .B query-raid-consistency-check .br Show all RAID arrays that are candidates for a RAID consistency check. .TP .B query-format-for-jbod .br Show all disks that can be reformatted for JBOD function. .TP .B query-reclaim .br Show all IOAs that may need a reclaim cache storage. .TP .B query-arrays-raid-include .br Show all RAID arrays that can have disks included in them to increase their capacity. .TP .B query-devices-raid-include [array] .br Show all disks that can be added to the specified array to increase its capacity. .TP .B query-supported-raid-levels [IOA] .br Show all RAID levels supported by the specified adapter. .TP .B query-include-allowed [IOA] [raid level] .br Some RAID levels allow for adding additional disks to existing disk arrays to increase their capacity. Prints "yes" to stdout if the specified RAID level supports this function, else prints "no". .TP .B query-max-devices-in-array [IOA] [raid level] .br Print the maximum number of devices allowed in a RAID array of the specified RAID level for the specified RAID adapter. .TP .B query-min-devices-in-array [IOA] [raid level] .br Print the minimum number of devices allowed in a RAID array of the specified RAID level for the specified RAID adapter. .TP .B query-min-mult-in-array [IOA] [raid level] .br Print the minimum multiple of devices required in a RAID array of the specified RAID level for the specified RAID adapter. .TP .B query-supp-stripe-sizes [IOA] [raid level] .br Print all supported stripe sizes supported for RAID arrays of the specified RAID level on the specified RAID adapter. Stripe sizes are printed in units of KB. .TP .B query-recommended-stripe-size [IOA] [raid level] .br Print the default/recommended stripe size for RAID arrays of the specified RAID level on the specified RAID adapter. Stripe size is in units of KB. .TP .B query-recovery-format .br Show all disks that can be formatted for error recovery purposes. .TP .B query-raid-rebuild .br Show all disks in RAID arrays that can be rebuilt. .TP .B query-format-for-raid .br Show all disks that can be formatted such that they can be used in a RAID array or as a hot spare. .TP .B query-ucode-level [device] .br Show the microcode level that is currently loaded on the specified device. Note: The device specified may be the sg device associated with an IOA, in which case the IOA's microcode level will be shown. .TP .B query-format-timeout [device] .br Show the current format timeout to be used when formatting the specified disk. This value is only applicable when the device is currently in Advanced Function format. .TP .B query-qdepth [device] .br Show the queue depth currently being used for the specified disk. .TP .B query-tcq-enable [device] .br Print 1 to stdout if tagged queuing is enabled for the specified device, else print 0 to stdout. .TP .B query-log-level [IOA] .br Print the current log level being used for the specified IOA. Can be a number from 0 to n. .TP .B query-add-device .br Show all empty disk slots that can have a disk concurrently added. .TP .B query-remove-device .br Show all disk slots which are either empty or have disks in them which can be concurrently removed from the running system. .TP .B query-initiator-id [IOA] [busno] .br Show the current SCSI initiator ID used by the IOA for the specified SCSI bus. .TP .B query-bus-speed [IOA] [busno] .br Show the current maximum SCSI bus speed allowed on the specified SCSI bus. .TP .B query-bus-width [IOA] [busno] .br Show the current SCSI bus width in units of bits for the specified SCSI bus. .TP .B query-path-status [IOA] .br Show the current dual path state for the SAS devices attached specified IOA. .TP .B query-path-details [device] .br Show the current dual path details for the specified SAS device. .TP .B query-arrays-raid-migrate .br Show the arrays that can be migrated to a different protection level. .TP .B query-devices-raid-migrate [array] .br Show the AF disks that are candidates to be used in a migration for a given array. .TP .B query-raid-levels-raid-migrate [array] .br Show the protection levels to which the given array can be migrated. .TP .B query-stripe-sizes-raid-migrate [array] [raid level] .br Given an array and a protection level, show the valid stripe sizes to which the array can be migrated. .TP .B query-devices-min-max-raid-migrate [array] [raid level] .br Show the number of devices that will be removed for a migration to a protection level that requires fewer devices. Or, show the minmum number of devices required, the maximum number of devices allowed and the multiple of the number of devices required for a migration that requires more devices. .TP .B query-ioas-asymmetric-access .br Show the IOAs that support asymmetric access. .TP .B query-arrays-asymmetric-access .br Show the disk arrays that are candidates for setting their asymmetric access mode to Optimized or Non-Optimized. .TP .B query-ioa-asymmetric-access-mode [IOA] .br Show the current asymmetric access mode for the given IOA. .TP .B query-array-asymmetric-access-mode [array] .br Show the current asymmetric access mode for the given disk array. .TP .B query-ioa-caching [IOA] .br Show whether or not the user requested caching mode for the given IOA is set to default or disabled. .TP .B raid-create [-r raid_level] [-s stripe_size_in_kb] [devices...] Create a RAID array. RAID level can be any supported RAID level for the given adapter, such as 0, 10, 5, 6. Currently supported stripe sizes in kb include 16, 64, and 256. If raid_level is not specified, it will default to RAID 5. If stripe size is not specified, it will default to the recommended stripe size for the selected RAID level. Devices are specified with their full name, either the /dev/sd name or the /dev/sg name is acceptable. Example array creation: .br .B iprconfig -c raid-create -r 5 -s 64 /dev/sda /dev/sdb /dev/sdc .br This would create a RAID 5 array with a 64k stripe size using the specified devices. .TP .B raid-delete [RAID device] Delete the specified RAID array. Specify either the /dev/sd name or the /dev/sg name. Only 1 array can be deleted with a single command. Example: .br .B iprconfig -c raid-delete /dev/sda .br This would delete the disk array represented by /dev/sda .TP .B raid-include [array] [disk] ... [disk] .br Add the specified devices to the specified disk array to increase its capacity. Example: .br .B iprconfig -c raid-include sda sg6 sg7 .TP .B raid-migrate -r raid_level [-s stripe_size_in_kb] array [disk] ... [disk] .br Migrate an existing RAID array to a new RAID protection level. Optionally, a new stripe size can be given. In some cases one or more new disks must be added for the migration to succeed. Example: .br .B iprconfig -c raid-migrate -r 10 -s 64 sda sg5 sg6 .TP .B format-for-raid [disk] ... [disk] .br Format the specified disks for Advanced Function so they can be used in a RAID array or as a hot spare. .TP .B format-for-jbod [disk] ... [disk] .br Format the specified disks for JBOD Function so they can be used as standalone disks. .TP .B recovery-format [disk] ... [disk] .br Format the specified disks as directed by the reference guide for error recovery purposes. .TP .B hot-spare-create [disk] .br Create a hot spare using the specified Advanced Function disk. .TP .B hot-spare-delete [disk] .br Delete the specified hot spare. .TP .B disrupt-device [disk] .br Force the specified Advanced Function device failed. .TP .B reclaim-cache [IOA] .br Reclaim the specified IOA's write cache. .B ATTENTION: Use this option with care. This is used to discard data from the cache and may result in data loss. This option is designed to be used by authorized IBM hardware customer engineers. .TP .B reclaim-unknown-cache [IOA] .br Reclaim the specified IOA's write cache and allow unknown data loss. .B ATTENTION: Use this option with care. This is used to discard data from the cache and WILL result in data loss. This option is designed to be used by authorized IBM hardware customer engineers. .TP .B raid-consistency-check [array] .br Force a full RAID consistency check on the specified array. This command will return before the RAID consistency check has completed. Use the .B status command to check the status of the command. .TP .B raid-rebuild [disk] .br Following a disk replacement for a failed disk in a RAID array, use this command to rebuild the failed disk's data onto the new disk and return the disk array to the .B Active state. .TP .B update-ucode [device] [microcode file] .br Update the microcode on the specified device (IOA or disk) with the specified microcode file. .B ATTENTION: Limited checking of the microcode image is done. Make sure the specified microcode file is the correct file for the specified device. .TP .B set-format-timeout [disk] [timeout in hours] .br Set the format timeout to be used when formatting the specified disk. .TP .B set-qdepth [device] [queue depth] .br Set the queue depth for the specified device or disk array. .TP .B set-tcq-enable [device] [0 = disable, 1 = enable] .br Enable/disable tagged command queueing for the specified device. .TP .B set-log-level [IOA] [log level] .br Set the error logging verbosity to use for the specified IOA. Default is 2. .TP .B identify-disk [disk] [0 = turn off identify LED, 1 = turn on identify LED] .br Turn on/off the disk identify LED for the specified disk. This function may or may not be available depending on the hardware packaging. .TP .B identify-slot [location] [0 = turn off identify LED, 1 = turn on identify LED] .br Turn on/off the disk identify LED for the specified location. This function may or may not be available depending on the hardware packaging. Example: .br .B iprconfig -c identify-slot 0000:d8:01.0/0:1:1: 1 .TP .B remove-disk [disk] [0 = turn off identify LED, 1 = turn on identify LED] .br Turn on/off the disk remove identify LED for the specified device. When 1 is specified as the second argument, the specified disk is set to the remove state. When in this state, the disk may be removed. Once the disk has been physically removed, iprconfig must be invoked again with the second argument set to 0. This turns off the slot identifier light and logically removes the disk from the host operating system. .TP .B remove-slot [location] [0 = turn off identify LED, 1 = turn on identify LED] .br Turn on/off the disk remove identify LED for the specified location. When 1 is specified as the second argument, the specified location is set to the remove state. When in this state, the disk may be removed. Once the disk has been physically removed, iprconfig must be invoked again with the second argument set to 0. This turns off the slot identifier light and logically removes the disk from the host operating system. Example: .br .B iprconfig -c remove-slot 0000:d8:01.0/0:1:1: 1 .TP .B add-slot [location] [0 = turn off identify LED, 1 = turn on identify LED] .br Turn on/off the disk insert identify LED for the specified location. When 1 is specified as the second argument, the specified location is set to the insert state. When in this state, the disk may be inserted. Once the disk has been physically inserted, iprconfig must be invoked again with the second argument set to 0. This turns off the slot identifier light and logically adds the disk to the host operating system. Example: .br .B iprconfig -c add-slot 0000:d8:01.0/0:1:1: 1 .TP .B set-initiator-id [IOA] [busno] [initiator id] .br Set the IOA's SCSI initiator ID for the specified bus. Must be a value between 0 and 7 and must not conflict with any other device on the SCSI bus. .TP .B set-bus-speed [IOA] [busno] [speed in MB/sec] .br Set the maximum SCSI bus speed allowed on the specified SCSI bus. .TP .B set-bus-width [IOA] [busno] [bus width in # bits] .br Set the SCSI bus width to use for the specified SCSI bus. Example: .br .B iprconfig -c set-bus-width sg5 0 16 .TP .B primary [IOA] .br Set the adapter as the .B preferred primary adapter. This is used in dual initiator RAID configurations to indicate which adapter should be the .B primary adapter. The .B primary adapter should be the adapter receiving the majority of the I/O. Example: .br .B iprconfig -c primary sg5 .TP .B secondary [IOA] .br Set the adapter to indicate it is not the .B preferred primary adapter. See the notes for the .B preferred primary for additional information. Example: .br .B iprconfig -c secondary sg5 .TP .B set-all-primary .br Set all attached ipr adapters as the .B preferred primary adapter. This can be used when running a dual initiator RAID HA configuration. This command can be run on the .B primary system to quickly enable the .B preferred primary mode for all attached adapters. Refer to /etc/ha.d/resource.d/iprha for an example of how this might be used. Example: .br .B iprconfig -c set-all-primary .TP .B set-all-secondary .br Set all attached ipr adapters to indicate they are not the .B preferred primary adapter. Example: .br .B iprconfig -c set-all-secondary .TP .B query-ha-mode [IOA] .br When an adapter is configured in a highly available dual adapter configuration, it may be able to be configured in one of two ways. The default mode is .B Normal. This mode is used for all SCSI adapters and many SAS adapters. Some SAS adapters also support a .B JBOD dual adapter configuration. This mode is to be used when the dual adapter configuration is to consist of JBOD disks rather than RAID arrays. If the adapter is .B NOT going to be used in a dual adapter configuration, this mode .B MUST be set to .B Normal. Example: .br .B iprconfig -c query-ha-mode sg5 .TP .B set-ha-mode [IOA] [Normal | RAID] Used to set the high-availability mode of the adapter. Refer to the .B query-ha-mode command for more information regarding these settings. Example: .br .B iprconfig -c set-ha-mode sg5 Normal .TP .B set-array-asymmetric-access-mode [array] [Optimized | Non-Optimized] Used to set the asymmetric access mode of the disk array. Example: .br .B iprconfig -c set-array-asymmetric-access-mode sda Optimized .TP .B set-ioa-asymmetric-access-mode [IOA] [Enabled | Disabled] Used to set the asymmetric access mode of the IOA. .br Example: .br .B iprconfig -c set-ioa-asymmetric-access-mode sg5 Enabled .TP .B set-ioa-caching [IOA] [Default | Disabled] Used to set the requested caching mode of the IOA. .br Example: .br .B iprconfig -c set-ioa-caching sg5 Disabled .TP .B get-live-dump [IOA] Dump the IOA's implementation unique critical information. The dump data will be saved in the /var/log/ directory with the pattern ipr-CCIN-PCI_ADDRESS-dump-TIMESTAMP. .br Example: .br .B iprconfig -c get-live-dump sg5 .RE .TP .B \-\-version Print version number of .B iprconfig .TP .B \-\-debug Enable additional error logging. Enabling this will result in additional errors logging to /var/log/messages. .TP .B \-\-force Disable safety checks. Use this to disable safety checks in iprconfig. This will allow you to format devices that are not at the appropriate code levels. Only use this option if you really know what you are doing. .SH AUTHOR Brian King .SH NOTES .TP .B Notes on using under iSeries 5250 telnet .PP Only use this utility across 5250 telnet when there are no other options available to you. Since there may be occasions when 5250 telnet is your only option to access your iSeries Linux console, every attempt has been made to make this utility usable under 5250 telnet. By following a few guidelines, you can make your 5250 telnet experience more productive and much less frustrating. .PP 1. First of all, it will be helpful to know how the keys are mapped under 5250 telnet. From the 5250 telnet window, hit ESC. This will get you to the .B Send TELNET Control Functions menu. Take option 6 to display the keyboard map. Take note of how TAB, ESC, CTLC, and SENDWOCR are bound. They will be useful in the future. .PP 2. When selecting menu options, enter the menu number, followed by the enter key, same as usual. .PP 3. When typing single character commands (eg. r=Refresh), type the single character followed by the SENDWOCR key (F11 by default). .PP 4. When on a device/array/IOA selection screen (eg. Display Disk Unit Details), do NOT use the arrow keys to navigate. Instead use the TAB key (F7 by default) to navigate these screens. .PP 5. Beware of the backspace and delete keys. As a rule do NOT use them. .PP 6. When editing the root kernel message log directory or the default editor, you may use the arrow keys, but not the backspace and delete keys. Use the space bar to remove already typed characters. iprutils/iprinit.80000644000000000000000000000311212275251125013175 0ustar rootroot.\" (C) Copyright 2000, 2005 .\" International Business Machines Corporation and others. .\" All Rights Reserved. This program and the accompanying .\" materials are made available under the terms of the .\" Common Public License v1.0 which accompanies this distribution. .TH IPRINIT 8 "February 2005" .SH NAME iprinit - IBM Power RAID adapter/device initialization utility .SH SYNOPSIS .B iprinit .sp .BI "iprinit --debug --daemon --use-polling --use-uevents" .SH DESCRIPTION .B iprinit is used to setup IBM Power RAID adapters and devices for optimal performance. It will enable U320 scsi speeds if it is determined the disk enclosure supports it. It will also enable tagged command queuing. Any scsi bus attributes, such as bus width, or disk attributes, such as queue depth, that have been setup by iprconfig will be loaded as well. It is desired that this be called in the boot process, using the init.d script provided. .SH OPTIONS .TP .B \-\-version Print the version number of .B iprinit .TP .B \-\-debug Enable additional error logging. Enabling this will result in additional errors logging to /var/log/messages. .TP .B \-\-daemon Run as a daemon to handle adapters and devices being hotplugged. .TP .B \-\-use-polling Do not use netlink/uevent notification, but rather poll for adapter and device configuration changes. .TP .B \-\-use-uevents Use netlink/uevent notification rather than polling for adapter and device configuration changes. If not specified, polling will be used until the first uevent notification appears, then netlink will be used. .SH AUTHOR Brian King (brking@us.ibm.com) iprutils/LICENSE0000644000000000000000000002661612275251125012451 0ustar rootrootCommon Public License Version 1.0 THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 1. DEFINITIONS "Contribution" means: a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and b) in the case of each subsequent Contributor: i) changes to the Program, and ii) additions to the Program; where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. "Contributor" means any person or entity that distributes the Program. "Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. "Program" means the Contributions distributed in accordance with this Agreement. "Recipient" means anyone who receives the Program under this Agreement, including all Contributors. 2. GRANT OF RIGHTS a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. 3. REQUIREMENTS A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: a) it complies with the terms and conditions of this Agreement; and b) its license agreement: i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages such as lost profits; iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. When the Program is made available in source code form: a) it must be made available under this Agreement; and b) a copy of this Agreement must be included with each copy of the Program. Contributors may not remove or alter any copyright notices contained within the Program. Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. 4. COMMERCIAL DISTRIBUTION Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 5. NO WARRANTY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 6. DISCLAIMER OF LIABILITY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. GENERAL If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. iprutils/iprupdate.80000644000000000000000000000364112275251125013523 0ustar rootroot.\" (C) Copyright 2000, 2004 .\" International Business Machines Corporation and others. .\" All Rights Reserved. This program and the accompanying .\" materials are made available under the terms of the .\" Common Public License v1.0 which accompanies this distribution. .TH IPRUPDATE 8 "February 2005" .SH NAME iprupdate - IBM Power RAID adapter/device microcode update utility .SH SYNOPSIS .B iprupdate .sp .BI "iprupdate --debug --check_only --daemon --use-polling --use-uevents" .SH DESCRIPTION .B iprupdate is used to update the microcode on IBM storage adapters and the disk units attached to them to a minimum supported level. It can be run at any time while the ipr device driver is loaded. The utility will check the microcode revision level of each storage adapter/disk unit on the system and compare it to the microcode revision level that is installed in /etc/microcode, /usr/lib/microcode, and /lib/firmware. If the microcode level on the adapter/device is below the minimum supported level and the level of the latest image is newer, the microcode will be updated to the latest level available. It is desired that this be called in the boot process. .SH OPTIONS .TP .B \-\-version Print the version number of .B iprupdate .TP .B \-\-debug Enable additional error logging. Enabling this will result in additional errors logging to /var/log/messages. .TP .B \-\-check_only Check if any adapters or disk units are below their minimum supported level. .TP .B \-\-daemon Run as a daemon to handle adapters and devices being hotplugged. .TP .B \-\-use-polling Do not use netlink/uevent notification, but rather poll for adapter and device configuration changes. .TP .B \-\-use-uevents Use netlink/uevent notification rather than polling for adapter and device configuration changes. If not specified, polling will be used until the first uevent notification appears, then netlink will be used. .SH AUTHOR Brian King (brking@us.ibm.com) iprutils/spec/0000755000000000000000000000000012275251164012366 5ustar rootrootiprutils/spec/iprutils.spec0000644000000000000000000006157512275251164015133 0ustar rootrootSummary: Utilities for the IBM Power Linux RAID adapters Name: iprutils Version: 2.4.0 Release: 1 License: CPL Group: System Environment/Base Vendor: IBM URL: http://sourceforge.net/projects/iprdd/ Source0: iprutils-%{version}-src.tgz BuildRoot: %{_tmppath}/%{name}-root %description Provides a suite of utilities to manage and configure SCSI devices supported by the ipr SCSI storage device driver. %prep %setup -q -n %{name} %build make %install make INSTALL_MOD_PATH=$RPM_BUILD_ROOT install install -d $RPM_BUILD_ROOT/%{_sysconfdir}/init.d install -m 755 init.d/iprinit $RPM_BUILD_ROOT/%{_sysconfdir}/init.d/iprinit install -m 755 init.d/iprdump $RPM_BUILD_ROOT/%{_sysconfdir}/init.d/iprdump install -m 755 init.d/iprupdate $RPM_BUILD_ROOT/%{_sysconfdir}/init.d/iprupdate install -d $RPM_BUILD_ROOT/%{_sysconfdir}/ha.d install -d $RPM_BUILD_ROOT/%{_sysconfdir}/ha.d/resource.d install -m 755 init.d/iprha $RPM_BUILD_ROOT/%{_sysconfdir}/ha.d/resource.d/iprha %ifarch ppc ppc64 %post if [ $1 = 2 ]; then echo "Restarting iprutils services" %{_sysconfdir}/init.d/iprdump restart > /dev/null 2>&1 %{_sysconfdir}/init.d/iprupdate restart > /dev/null 2>&1 %{_sysconfdir}/init.d/iprinit restart > /dev/null 2>&1 elif [ -f /sbin/chkconfig ]; then /sbin/chkconfig --add iprinit > /dev/null 2>&1 /sbin/chkconfig --add iprdump > /dev/null 2>&1 /sbin/chkconfig --add iprupdate > /dev/null 2>&1 /sbin/chkconfig iprinit on > /dev/null 2>&1 /sbin/chkconfig iprdump on > /dev/null 2>&1 /sbin/chkconfig iprupdate on > /dev/null 2>&1 else /usr/lib/lsb/install_initd %{_sysconfdir}/init.d/iprinit /usr/lib/lsb/install_initd %{_sysconfdir}/init.d/iprdump /usr/lib/lsb/install_initd %{_sysconfdir}/init.d/iprupdate fi %endif %ifarch ppc ppc64 %preun if [ $1 = 0 ]; then %{_sysconfdir}/init.d/iprdump stop > /dev/null 2>&1 %{_sysconfdir}/init.d/iprupdate stop > /dev/null 2>&1 %{_sysconfdir}/init.d/iprinit stop > /dev/null 2>&1 if [ -f /sbin/chkconfig ]; then /sbin/chkconfig --del iprinit > /dev/null 2>&1 /sbin/chkconfig --del iprdump > /dev/null 2>&1 /sbin/chkconfig --del iprupdate > /dev/null 2>&1 else /usr/lib/lsb/remove_initd %{_sysconfdir}/init.d/iprdump /usr/lib/lsb/remove_initd %{_sysconfdir}/init.d/iprupdate /usr/lib/lsb/remove_initd %{_sysconfdir}/init.d/iprinit fi fi %endif %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) %doc README LICENSE /sbin/* %{_mandir}/man*/* %{_sysconfdir}/init.d/* %{_sysconfdir}/ha.d/resource.d/iprha %changelog * Fri Feb 07 2014 Wen Xiong 2.4.0 - Release 2.4.0 - Eliminate libsysfs dependency * Fri Feb 07 2014 Wen Xiong 2.3.18 - Release 2.3.18 - array start enhancement patch - compliation fails on ununtu * Fri Feb 01 2014 Wen Xiong 2.3.17 - Release 2.3.17 - creating an array fails for T2 disks. - Several fixes for LE system. - Enable actie/active by default * Tue Nov 05 2013 Wen Xiong 2.3.16 - Release 2.3.16 - Fixes a bug for 4K bytes/sector disks in iprutils - Fixes a bug for disk hot swap in VSES on P8 - Release 2.3.15 - Add support for 4K bytes/sector disks in iprutils - Release 2.3.14 - Fixes stale information after hot plug a disk into an array - Segmentation fault when removing a disk with hot spare disk - Fxied sysfs error when updating microcode - Fixes the platform location issue for Tres drawer - Fix hop count defines - Release 2.3.13 - Fixes Platform Location for 32bit adapter. - Adds support for optical devices. - Fixes platform location for disks. - Changes %post and %preun sections of spec file. - Fixes release date on the spec file. * Fri Sep 05 2012 Wen Xiong 2.3.12 - Release 2.3.12 - Addes supporting suspend/resume utility for BlueHawk. - Fixes raid array delete error. * Tue Jun 12 2012 Kleber Sacilotto de Souza 2.3.11 - Release 2.3.11 - Fixes deleting an array logs I/O errors. - Fixes buffer overflow in disk details. - Fixes wrong status on an array for the secondary controller. - Fixes add_string_to_body() for non-interactive interface. - Fixes IOA asymmetric access mode change. - Gets location code when SES page 4 is unavailable. - Fixes physical location error if disk is vses disk. * Mon Apr 02 2012 Kleber Sacilotto de Souza 2.3.10 - Release 2.3.10 - Displays Hardware Location Code for disk/expander. - Changes concurrent maintenance GUI to 3 ways toggle. - Changes README for concurrent maintenance command line. - Adds structures and functions for query resource paths aliases. - Fixes a bug in suspend/resume device bus. - Supports concurrent maintenance for 64bit adapter. - Supports not physical remove disk. - Fixes header problem when query-remove/add-device. - Sometimes receive diag failed at first time after update adapter firmware. - If the disk in a RAID, iprutils itself has to delete sysfs name for secondary adapter. - Old drawers don't support query page 4 for physical location. - Adds display sas path detail for 64bit adapter. - Fixes GUI version of raid consistency check. - Adds new chip details entries. - Fixes SES microcode update failures. - Fixes SES microcode image version on CLI. - Adds a flag to raid create command for known zeroed drives. * Fri Dec 16 2011 Kleber Sacilotto de Souza 2.3.9 - Release 2.3.9 - Fixes disks and arrays status. - Uses C4 inquiry page for DRAM size. - Sets allow_restart to zero before raid delete. - Fixes raid consistency check for 64 bit adapters. - Fixes compile warning. - Updates queue depth values. * Thu Nov 24 2011 Kleber Sacilotto de Souza 2.3.8 - Release 2.3.8 - Adds support for FPGA updates. - Bumps up number of command status records. - Only uses primary adapter for initialize and format disk task. - Fixes check for hotspare devices. - Removes unnecessary call to find_dev(). * Thu Aug 18 2011 Kleber Sacilotto de Souza 2.3.7 - Release 2.3.7 - Adds support for vset device asymmetric config. - Fixes display details for array devices. - Increases buffer size for new adapter config data. - Fixes query command status functionality. * Thu Jul 14 2011 Kleber Sacilotto de Souza 2.3.6 - Release 2.3.6 - Fixes segmentation fault when qac is not available. - Fixes serial number comparison. * Thu Jun 16 2011 Kleber Sacilotto de Souza 2.3.5 - Release 2.3.5 - Fixes suspend and resume device bus commands. * Wed Apr 27 2011 Kleber Sacilotto de Souza 2.3.4 - Release 2.3.4 - Fixes data types for C4 page definition. - Fixes setting qac cdb[3] and rename ipr_is_array_record. - Fixes array migration funcionality. - Increases maximum dump size. * Mon Mar 07 2011 Wayne Boyer 2.3.3 - Release 2.3.3 - Adds initial support for sis64 asymmetric access changes. - Fixes segmentation faults when trying to display certain information. - Fixes some file permissions and a compilation warning. * Thu Nov 18 2010 Kleber Sacilotto de Souza 2.3.2 - Release 2.3.2 - Fixes code incompatibility with libsysfs version 1. - Changes get_scsi_max_xfer_len return value. * Fri Nov 12 2010 Kleber Sacilotto de Souza 2.3.1 - Release 2.3.1 - Fixes display of empty slots. - Fixes IOA description indentation. - Adds Resource Path display for SIS64 devices. - Adds support for the new live dump functionality. - Fixes mode sense page24 for little endian architectures. - Fixes battery info for little endian architectures. * Fri Aug 12 2010 Kleber Sacilotto de Souza 2.3.0 - Release 2.3.0 - Fixes type of pointers provided to scandir. - Fixes buffer overflow when the SCSI Host ID is equal or greater than 1000. - Fixes function names on comments in iprlib.c. - Fixes memory leak when ioctl() returns EINVAL. - Adds support to identify sis64 capability. - Adds support for new sysfs attributes. - Adds support for type 3 QAC command. - Adds support for type 3 records. * Mon May 10 2010 Kleber Sacilotto de Souza 2.2.21 - Release 2.2.21 - Fixes firmware image files left open after getting firmware level. - Changes IOA DRAM size display from hex to decimal base. - Handles SG_IO ioctl error with older distros which caused disk microcode download to fail with JBOD's. * Tue Apr 07 2010 Kleber Sacilotto de Souza 2.2.20 - Release 2.2.20 - Adds resource address parsing based on encoding format flag. * Fri Mar 19 2010 Kleber Sacilotto de Souza 2.2.19 - Release 2.2.19 - Fixes platform location display on blades. * Fri Oct 30 2009 Kleber Sacilotto de Souza 2.2.18 - Release 2.2.18 - Changes the default write buffer mode for writing to SES devices. - Fixes the CLI show-details command for SES devices. - Comments out the get_write_buffer_dev() routine to suppress compilation warnings as it is not being used at the moment. - Fixes the Platform Location Information display for hotplug adapters and displays the information for non-hotplug adapters. - Fixes the indentation problem when IOA host number is equal to or greater than 10. * Wed Sep 16 2009 Wayne Boyer 2.2.17 - Release 2.2.17 - Fixes a NULL pointer dereference which caused the daemons to silently fail. * Fri Aug 14 2009 Wayne Boyer 2.2.16 - Release 2.2.16 - Fixes a bug where CLI raid create is broken. - Fixes several potential memory leak problems. - Adds man page entries for the disable cache query and command. * Tue Jun 30 2009 Wayne Boyer 2.2.15 - Release 2.2.15 - Fixes a bug where cache reclaim can time out too soon. - Fixes some problems with the disable caching support. * Tue Apr 07 2009 Wayne Boyer 2.2.14 - Release 2.2.14 - Add support for disabling IOA caching. - Remove #include of as it is not used. - Allow giving a relative path for a microcode image file when doing an upgrade from the command line. - Add support for SSD drives. * Mon Nov 17 2008 Wayne Boyer 2.2.13 - Release 2.2.13 - Additional fixes for the active-active functionality. - Fixes for iprdump to recognize a /sys/devices path. * Thu Oct 23 2008 Wayne Boyer 2.2.12 - Release 2.2.12 - Additional GUI support and fixes for the active-active functionality. - Fixes for CLI RAID create and delete. * Fri Sep 26 2008 Wayne Boyer 2.2.11 - Release 2.2.11 - Add support for the active-active functionality. * Wed Aug 20 2008 Wayne Boyer - Fixed a compilation failure on small endian systems. * Wed Aug 13 2008 Wayne Boyer 2.2.10 - Release 2.2.10 - Add support for the array migration functionality. * Wed May 14 2008 Tseng-Hui Lin - Under heavy traffic, download microcode to a disk through /dev/sgX may get -ENOMEM SG_IO ioctl. Change to use /dev/sdX. Fall back to /dev/sgX if: (1) /dev/sdX does not exist or (2) max_sectors_kb is not set large enough for microcode download. * Thu Apr 09 2008 Tseng-Hui Lin 2.2.9 - Release 2.2.9 - Do not save preferred primary attribute to fix an infinite failover problem in HA two system RAID configuration. * Thu Sep 20 2007 Brian King 2.2.8 - Release 2.2.8 * Mon Aug 27 2007 Brian King - Add support for setting dual adapter HA mode. * Thu Jun 21 2007 Brian King 2.2.7 - Fixes for init.d scripts to fix problems installing them on some systems. * Wed Jun 13 2007 Brian King - rpm spec file updates * Tue May 1 2007 Brian King 2.2.6 - Fix iprinit dual initiator failover device rescanning code. * Wed Apr 25 2007 Brian King - Add iprha init.d script to enable/disable primary adapter mode for dual initiator configs. * Fri Apr 13 2007 Brian King - Add adapter config option in iprconfig for setting primary/secondary adapter in dual adapter environment. * Mon Mar 19 2007 Brian King - Add iprconfig option to display SAS dual pathing information. * Mon Mar 12 2007 Brian King - Increase time waiting for new devices to show up in iprconfig when doing concurrent add. * Fri Feb 9 2007 Brian King - Add filename date to microcode download screen. (Ryan Hong) - Fix to prevent unnecessarily writing sysfs attributes. (Ryan Hong) * Tue Feb 6 2007 Brian King - Return standard error code according to send_dev_init() return code. * Mon Feb 5 2007 Brian King - Fix incorrect memory free in analyze log menus. * Wed Jan 10 2007 Brian King - Fix send diagnostics buffer transfer length to be only what was received in the receive diagnostics. Fixes disk hotplug on some SAS disk enclosures. * Thu Jan 4 2007 Brian King - Sourceforge patch 1627673: iprutils fix to buffer overflow - Add checking to iprconfig command "set-qdepth" input queue depth value. If the given value is larger than 255, fail the command. - Fix a bug in which the iprconfig command "raid-create" may create an illegal queue depth value to the ipr config file. * Thu Jan 4 2007 Brian King - Sourceforge patch 1627672: iprutils fix to buffer overflow - Fix a bug in which the iprconfig command "raid-create" may create an illegal queue depth value to the ipr config file. * Wed Nov 29 2006 Brian King - Fixes for SES microcode download on SAS. * Mon Nov 20 2006 Brian King - Change to handle UA responses in the JBOD iprinit sequence. * Thu Nov 16 2006 Brian King - Fix segfault in iprconfig if /var/log does not exist. - Allow IOA microcode download to a secondary IOA. - Fix to prevent errors during SAS SES microcode download. * Tue Oct 10 2006 Brian King - Add a couple utility functions for SAS ipr_query_sas_expander_info ipr_query_res_redundancy_info * Wed Sep 27 2006 Brian King 2.2.3 - Fix SAS disk hotplug dual path bug. * Tue Sep 12 2006 Brian King - Change default QERR setting for SAS to 0. * Tue Sep 12 2006 Brian King 2.2.2 - Fix iprconfig set-format-timeout. * Mon Sep 11 2006 Brian King 2.2.1 * Fri Sep 8 2006 Brian King - Reduce default JBOD queue depth to 3. - Fix iprconfig -c set-bus-speed. * Wed Aug 23 2006 Brian King - Fix a race condition with hotplug events which could cause the ipr daemons to run before newly added devices are completed added to the system. * Wed Aug 9 2006 Brian King - Fix a segfault in iprdbg when using the macro function * Tue Jul 25 2006 Brian King 2.2.0 - Fix for command line SES microcode update. * Mon May 8 2006 Brian King - Use IOA's default format timeout for AF DASD instead of using a hard coded default. - Remove RAID support for some older drives that should never have been supported. * Mon May 1 2006 Brian King - Add support to iprinit for it to handle disks going from JBOD <-> AF format across an adapter reset. When this is detected, iprinit will now attempt to delete the disk and then rescan that slot. - Fixed an ncurses screen drawing bug which resulted in the screen getting paged down if the cursor was on the last item on the screen and 't' was pressed to toggle the display. - Added disk concurrent maintenance support for handling dual pathed SAS disks. * Fri Mar 17 2006 Brian King - Improve iprdbg's logging * Thu Mar 16 2006 Brian King - Print better status for devices when IOA is offline/dead. * Tue Mar 14 2006 Brian King - Fix to allow for compiling with libsysfs 2.0.0 * Tue Mar 14 2006 Brian King 2.1.4 - Concurrent maintenance fix for certain iSeries enclosures which would result in non existent drive slots being displayed in iprconfig. * Wed Mar 8 2006 Brian King - Remove some redundant code in disk hotplug path * Thu Mar 2 2006 Brian King - Fixup status of RAID 10 arrays to print a better status under multiple failure scenarios. * Thu Feb 24 2006 Brian King 2.1.3 - Prevent duplicate mode sense commands from being issued. - More uevent handling improvements. - Automatically create hotplug directory if it doesn't already exist so adapter microcode update works. * Thu Feb 9 2006 Brian King - Improve robustness of uevents failure handling. Fall back to polling method if needed. * Fri Feb 3 2006 Brian King - Auxiliary cache adapter fixes. * Fri Jan 27 2006 Brian King - Fix iprconfig -c update-ucode to properly report an error if the wrong microcode level is specified. * Mon Jan 23 2006 Brian King - Fixed a compiler issue. * Fri Jan 20 2006 Brian King - Fixed a bug in iprconfig query-raid-create that prevented JBOD candidates from being displayed if there were no AF candidates as well. * Thu Jan 5 2006 Brian King 2.1.2 - Make iprupdate return success/failure indication when invoked with --force. * Tue Jan 3 2006 Brian King - Concurrent maintenance fix for 7031-D24/T24. * Tue Dec 20 2005 Brian King 2.1.1 - Fix compile error in iprconfig * Thu Dec 18 2005 Brian King 2.1.0 - Updates for aux cache IOAs - Updates for SAS adapters - Misc fixes for new iprconfig command line options * Wed Dec 7 2005 Brian King - Add command line options to iprconfig to perform virtually every iprconfig function available in the ncurses interface. * Tue Nov 15 2005 Brian King 2.0.15.6 - Fix concurrent maintenance with disk drawers reporting multiple SES devices on the same SCSI bus. * Fri Oct 7 2005 Anton Blanchard - Fix string length calculation in ipr_get_hotplug_dir * Wed Aug 17 2005 Brian King 2.0.15.4 - Fix a couple of uninitialized variable compile errors * Wed Jul 27 2005 Brian King 2.0.15.3 - Fix: iprconfig: IOA microcode update would leave AF DASD (disks that are in disk arrays) in a state where they were no longer tagged queueing. Fix iprconfig to run iprinit on the adapter after a microcode download to ensure all attached devices are properly setup after a microcode download. - Fix iprinit: If an IOA was reset for some reason at runtime, this would cause AF DASD devices to get tagged queueing turned off and it would never get turned back on. Change iprinit to detect this and turn tagged queueing back on if this happens. - Changing the queue depth for a disk array was broken. Fix iprinit to properly restore the queue depth from the ipr configuration file. - Fix iprconfig to handle disk format failures better - Fix potential iprutils segfaults when iterating over disk arrays * Wed Jun 1 2005 Brian King 2.0.15.1 - Fix iprconfig Analyze Log options * Wed May 18 2005 Brian King 2.0.15 - Clarify format options - Setup mode page 0 for IBM drives to ensure command aging is enabled. This ensures commands are not starved on some drives. - Fix so that iprdump properly names dump files once 100 dumps have been made. - Make iprconfig handle failures of scsi disk formats better - Fix iprconfig Set root kernel message log directory menu - Properly display RAID level on all iprconfig screens - Don't disable init.d daemons on an rpm -U * Tue Apr 12 2005 Brian King 2.0.14.2 - Fixed bug preventing disk microcode update from working. * Mon Apr 4 2005 Brian King - Add ability to force RAID consistency check * Mon Mar 25 2005 Brian King 2.0.14.1 - Removed mention of primary/secondary adapters in some error screens since multi-initiator RAID is not supported and the messages will just cause confusion. * Mon Mar 24 2005 Brian King - iprconfig: During disk hotplug, wait for sd devices to show up. Fixes errors getting logged by iprconfig during hotplug. * Mon Mar 23 2005 Brian King - iprconfig: Fix cancel path on concurrent add/remove of disks - Don't display current bus width and speed for SAS disks * Mon Mar 21 2005 Brian King - Fix scoping bug caught by gcc 4.0. * Fri Mar 18 2005 Brian King - Stop iprupdate from continually logging errors for adapters with backlevel adapter firmware. * Mon Mar 7 2005 Brian King 2.0.14 - Add support for non-interactive array creation and deletion through iprconfig. - Use kobject_uevent notifications instead of polling if the kernel supports it. - Fix iprconfig to set the actual queue depth for advanced function disks - Allow user to force tagged queuing on to drives that do not support QERR=1. - Fix handling of medium format corrupt drives for drives - iprconfig: Download microcode. Fix blank screen when displaying lots of microcode images. - Fix iprinit to wait for scsi generic devices to show up in case we are racing with hotplug. Fixes the following error: 0:255:0:0: Mode Sense failed. rc=1, SK: 5 ASC: 24 ASCQ: 0 - Add "known to be zeroed" tracking to iprconfig to drastically reduce the time required to create a RAID array when starting with 512 formatted disks - Add ability to query multi-adapter status for dual initiator RAID configs - Add ability to set "preferred primary" adapter when running dual initiator RAID configs - Add iprconfig screen to display dual adapter status for dual initiator RAID configs - Prevent RAID configuration from occurring on "secondary" adapter in dual initiator RAID configs - Use /dev/sd for SG_IO instead of /dev/sg when possible - Set QERR=3 rather than 1 for multi-initiator configurations - Set TST=1 for multi-initiator configurations - Allow Format device for JBOD function to work for JBOD adapters - Fix handling of dead adapters in all of iprutils. - Fix iprconfig RAID start bug for systems with multiple RAID adapters. - Fix iprconfig RAID include bug for systems with multiple RAID adapters. - Fix failing array add device due to race condition with iprinit. * Tue Oct 5 2004 Brian King 2.0.13 - Improve iprupdate error logs to indicate where to download microcode from. - Set default tcq queue depth for AS400 disks to 16. - Don't log errors in iprdump if CONFIG_IPR_DUMP not enabled in the kernel - Fix sysfs parsing to handle new sdev target kernel change - Rescan JBOD devices following recovery format to make the device usable if it was originally in an unsupported sector size. - Display correct adapter serial number in iprconfig. - Support for microcode download to new adapters. - Support for iSeries disk microcode update using microcode images from the pSeries microcode website. * Fri Jun 11 2004 Brian King 2.0.12 - Fix bug preventing ucode download to iSeries disks from working * Thu Jun 10 2004 Brian King 2.0.11 - Fix segmentation fault in _sg_ioctl that was causing a silent failure of microcode update to disks. The microcode update would fail, but no error would be logged. The seg fault was in a child process, so the parent process kept running. * Thu May 23 2004 Brian King 2.0.10 - Don't let iprdbg sg ioctls be retried. - Add --force flag to iprconfig to allow user to workaround buggy drive firmware. - Don't initialize read/write protected disks - Fix some reclaim cache bugs - Don't setup Mode Page 0x0A if test unit ready fails * Thu May 2 2004 Brian King 2.0.9 - Add --debug option to all utilities - Make utilities behave better when ipr is not loaded - Fix dependencies in init.d scripts - Only enable init.d scripts on ppc - Don't log an error if ipr is not loaded * Mon Apr 28 2004 Brian King 2.0.8 - Fix to properly enable init.d scripts when the rpm is installed - Fix memory leak in code download path - Increase size of page 0 inquiry buffer so that extended vpd is displayed - Decrease write buffer timeout to 2 minutes * Wed Apr 16 2004 Brian King 2.0.7 - Load sg module in init.d scripts if not loaded - Load sg module in iprconfig if not loaded * Wed Apr 14 2004 Brian King 2.0.6 - Battery maintenance fixes. - Fix to properly display failed status for pulled physical disks. * Tue Apr 6 2004 Brian King 2.0.5 - Battery maintenance fixes. - Fix init.d scripts to work properly with yast runlevel editor. - Fix device details screen in iprconfig for Failed array members - Allow formatting devices even if qerr cannot be disabled * Tue Mar 29 2004 Brian King 2.0.4 - Fixed some sysfs calls that changed their calling interfaces * Tue Mar 17 2004 Brian King 2.0.2-3 - Fixed iprutils/README0000644000000000000000000001152312275251125012313 0ustar rootroot# (C) Copyright 2000, 2001 # International Business Machines Corporation and others. # All Rights Reserved. This program and the accompanying # materials are made available under the terms of the # Common Public License v1.0 which accompanies this distribution. This directory contains user space tools required by the ipr device driver. To install them, type 'make' and then 'make install'. You will need pciutils-devel, ncurses, ncurses-devel, and sysfsutils packages installed to perform the 'make'. For more detailed information about ipr and iprutils, refer to the man pages and the Linux reference guide (SA23-1327), which is available here: http://publib16.boulder.ibm.com/pseries/en_US/infocenter/base/HW_scsi_adapters.htm iprdump - A user program meant to be run in the background to gather debug information should an IOA failure occur. Ideally, this program should be started during boot time after file systems are loaded when the ipr SCSI driver is used. An init.d script is provided to do this. iprupdate - IOA/disk unit microcode update utility This tool requires no arguments and should be run after each time the device driver is loaded. It will query each adapter/disk unit for the microcode version and update adapters/disk units that are not currently at the minimum supported levels. An init.d script is provided to run this at boot time. iprinit - IOA/disk unit initialization utility This tool requires no arguments and should be run after each time the device driver is loaded. It will setup all ipr adapters and attached disks to run with optimal performance and load any adapter, disk, or scsi bus configurations saved by iprconfig. It enables U320 scsi speeds and enables tagged command queuing. iprconfig - IOA Configuration and Recovery tool This is an ncurses based utility used to configure ipr adapters and devices. The basic functions of this tool are: CONFIGURATION 1. Display hardware status. This can be used to display all ipr SCSI disks attached to your system. Using this option it is possible to obtain information about each device on the system. 2. Work with SCSI Bus Configuration. This allows flexibility in setting some SCSI Bus parameters such is Max Bus Throughput and host SCSI ID. 3. Work with Driver Configuration. Currently allows adjustment to the devices drivers log level. 4. Work with disk configuration. Allows setting per device attributes, such as queue depth. 5. Download microcode. Allows microcode to be downloaded to adapters and SCSI disks. RAID 6. Create a disk array. Used to create a disk array. 7. Delete a disk array. Allows you to delete existing arrays. Data stored on the devices will not be preserved after issuing this command. 8. Add a device to a disk array. This option allows devices of similar capacity to be included into an existing RAID 5 disk array. 9. Format Device for advanced function/Format Device for JBOD (Just a Bunch Of DASD) function. Disk devices can either be formatted to 512 bytes/sector or 522 bytes/sector. Devices must be formatted to 522 bytes/sector, or advanced function format, in order to be used in a disk array or as a hot spare. Devices not in a disk array can be formatted to 512 bytes/sector so they can be used directly by the operating system. 10. Create a hot spare. Configure a disk to become a hot spare, capable of being used by the adapter to automatically replace a failed device RECOVERY 12. Concurrent add device. Concurrently add a SCSI disk to a running system. 13. Concurrent remove device. Concurrently remove a SCSI disk from a running system. 14. Initialize and format disk. This allows you to issue a SCSI format command to SCSI disks. Proceed with caution when using this option. 15. Reclaim IOA cache storage. This is to be used by IBM hardware service personnel only. Use of this function is potentially dangerous and may delete data from the non-volatile write cache on the adapter. 16. Rebuild disk unit data. This option is generally used following concurrent maintenance. Select this option after a failing array member device has been replaced to reconstruct device as an active array member. 17. Work with Resources Containing Cache Battery Packs. A set of displays are used to view the current status of the Cache Battery on resources containing battery packs and allow maintanence actions to be performed against those resources when necessary. 18. Analyze Log. This allows you to view the error messages logged by the ipr device driver. Changes Log: 1. Concurrent maintience commands line changes from PCI location to physical location(3/12/2012). #iprconfig -c identify-slot "U5886.001.P915059-P1-D1 1" #iprconfig -c remove-slot "U5886.001.P915059-P1-D1 1" #iprconfig -c add-slot "U5886.001.P915059-P1-D1 1" 2. Work with disk enclosures #iprconfig -c query-disk-enclosure-status #iprconfig -c suspend-disk-enclosure sg8 #iprconfig -c resume-disk-enclosure sg8 iprutils/iprlib.h0000644000000000000000000021720712275251164013077 0ustar rootroot#ifndef iprlib_h #define iprlib_h /** * IBM IPR adapter utility library * * (C) Copyright 2000, 2004 * International Business Machines Corporation and others. * All Rights Reserved. This program and the accompanying * materials are made available under the terms of the * Common Public License v1.0 which accompanies this distribution. * */ /* * $Header: /cvsroot/iprdd/iprutils/iprlib.h,v 1.107 2009/06/30 23:32:40 wboyer Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IPR_DASD_UCODE_USRLIB 0 #define IPR_DASD_UCODE_ETC 1 #define IPR_DASD_UCODE_NOT_FOUND -1 #define IPR_DASD_UCODE_NO_HDR 1 #define IPR_DASD_UCODE_HDR 2 #define UCODE_BASE_DIR "/usr/lib/microcode" #define LINUX_UCODE_BASE_DIR "/lib/firmware" #define FIRMWARE_HOTPLUG_DIR_TAG "FIRMWARE_DIR" #define FIRMWARE_HOTPLUG_CONFIG_FILE "/etc/hotplug/firmware.agent" #define FIRMWARE_HOTPLUG_DEFAULT_DIR LINUX_UCODE_BASE_DIR #define IPRDUMP_DIR "/var/log/" #define IPR_JBOD_BLOCK_SIZE 512 #define IPR_DEFAULT_AF_BLOCK_SIZE 522 #define IPR_JBOD_4K_BLOCK_SIZE 4096 #define IPR_AF_4K_BLOCK_SIZE 4224 #define IOCTL_BUFFER_SIZE 512 #define IPR_MAX_NUM_BUSES 4 #define IPR_VENDOR_ID_LEN 8 #define IPR_PROD_ID_LEN 16 #define IPR_SERIAL_NUM_LEN 8 #define ESM_SERIAL_NUM_LEN 12 #define YL_SERIAL_NUM_LEN 12 #define IPR_DESCRIPTION_LEN 16 #define IPR_VPD_PLANT_CODE_LEN 4 #define IPR_VPD_CACHE_SIZE_LEN 3 #define IPR_VPD_DRAM_SIZE_LEN 3 #define IPR_VPD_PART_NUM_LEN 12 #define IPR_CCB_CDB_LEN 16 #define IPR_QAC_BUFFER_SIZE 200000 #define IPR_SIS32_QAC_BUFFER_SIZE 16000 #define IPR_INVALID_ARRAY_ID 0xFF #define IPR_IOA_RESOURCE_HANDLE 0xffffffff #define IPR_RECLAIM_NUM_BLOCKS_MULTIPLIER 256 #define IPR_SDB_CHECK_AND_QUIESCE 0x00 #define IPR_SDB_CHECK_ONLY 0x40 #define IPR_SDB_CHECK_AND_QUIESCE_ENC 0x0e #define IPR_RDB_UNQUIESCE_AND_REBALANCE 0x20 #define IPR_MAX_NUM_SUPP_INQ_PAGES 36 #define SES_MAX_NUM_SUPP_INQ_PAGES 7 #define IPR_DUMP_TRACE_ENTRY_SIZE 8192 #define IPR_MODE_SENSE_LENGTH 255 #define IPR_DEFECT_LIST_HDR_LEN 4 #define IPR_FORMAT_DATA 0x10 #define IPR_FORMAT_IMMED 2 #define IPR_ARRAY_CMD_TIMEOUT (2 * 60) /* 2 minutes */ #define IPR_INTERNAL_DEV_TIMEOUT (2 * 60) /* 2 minutes */ #define IPR_FORMAT_UNIT_TIMEOUT (3 * 60 * 60) /* 3 hours */ #define IPR_INTERNAL_TIMEOUT (30) /* 30 seconds */ #define IPR_SUSPEND_DEV_BUS_TIMEOUT (35) /* 35 seconds */ #define IPR_EVALUATE_DEVICE_TIMEOUT (2 * 60) /* 2 minutes */ #define IPR_WRITE_BUFFER_TIMEOUT (8 * 60) /* 8 minutes */ #define IPR_CACHE_RECLAIM_TIMEOUT (10 * 60) /* 10 minutes */ #define SET_DASD_TIMEOUTS_TIMEOUT (2 * 60) #define IPR_NUM_DRIVE_ELEM_STATUS_ENTRIES 50 #define IPR_DRIVE_ELEM_STATUS_EMPTY 5 #define IPR_DRIVE_ELEM_STATUS_POPULATED 1 #define IPR_DRIVE_ELEM_STATUS_UNSUPP 0 #define IPR_DRIVE_ELEM_STATUS_NO_ACCESS 8 #define IPR_TIMEOUT_SECOND_RADIX 0x0000 #define IPR_TIMEOUT_MINUTE_RADIX 0x4000 #define IPR_TIMEOUT_RADIX_MASK 0xC000 #define IPR_TIMEOUT_RADIX_IS_MINUTE(to) \ (((to) & IPR_TIMEOUT_RADIX_MASK) == IPR_TIMEOUT_MINUTE_RADIX) #define IPR_TIMEOUT_RADIX_IS_SECONDS(to) \ (((to) & IPR_TIMEOUT_RADIX_MASK) == IPR_TIMEOUT_SECOND_RADIX) #define IPR_TIMEOUT_MASK 0x3FFF #define IPR_IOA_DEBUG 0xDDu #define IPR_IOA_DEBUG_READ_IOA_MEM 0x00u #define IPR_IOA_DEBUG_WRITE_IOA_MEM 0x01u #define IPR_IOA_DEBUG_READ_FLIT 0x03u #define IPR_IOA_DEBUG_ENABLE_DBG_FUNC 0x0Au #define IPR_IOA_DEBUG_DISABLE_DBG_FUNC 0x0Bu #define IPR_STD_INQ_Z0_TERM_LEN 8 #define IPR_STD_INQ_Z1_TERM_LEN 12 #define IPR_STD_INQ_Z2_TERM_LEN 4 #define IPR_STD_INQ_Z3_TERM_LEN 5 #define IPR_STD_INQ_Z4_TERM_LEN 4 #define IPR_STD_INQ_Z5_TERM_LEN 2 #define IPR_STD_INQ_Z6_TERM_LEN 10 #define IPR_STD_INQ_PART_NUM_LEN 12 #define IPR_STD_INQ_EC_LEVEL_LEN 10 #define IPR_STD_INQ_FRU_NUM_LEN 12 #define IPR_START_STOP_STOP 0x00 #define IPR_START_STOP_START 0x01 #define IPR_READ_CAPACITY_16 0x10 #define IPR_SERVICE_ACTION_IN 0x9E #define IPR_MAINTENANCE_IN 0xA3 #define IPR_QUERY_MULTI_ADAPTER_STATUS 0x01 #define IPR_MAINTENANCE_OUT 0xA4 #define IPR_CHANGE_MULTI_ADAPTER_ASSIGNMENT 0x02 #define IPR_QUERY_RESOURCE_STATE 0xC2 #define IPR_QUERY_COMMAND_STATUS 0xCB #define IPR_SUSPEND_DEV_BUS 0xC8 #define IPR_RESUME_DEV_BUS 0xC9 #define IPR_IOA_SERVICE_ACTION 0xD2 #define IPR_QUERY_RES_ADDR_ALIASES 0x10 #define IPR_QUERY_RES_PATH_ALIASES 0x20 #define IPR_DISRUPT_DEVICE 0x11 #define IPR_QUERY_SAS_EXPANDER_INFO 0x12 #define IPR_QUERY_RES_REDUNDANCY_INFO 0x13 #define IPR_QUERY_RES_REDUNDANCY_INFO64 0x23 #define IPR_CHANGE_CACHE_PARAMETERS 0x14 #define IPR_QUERY_CACHE_PARAMETERS 0x16 #define IPR_LIVE_DUMP 0x31 #define IPR_QUERY_IOA_DEV_PORT 0x32 #define IPR_EVALUATE_DEVICE 0xE4 #define SKIP_READ 0xE8 #define SKIP_WRITE 0xEA #define QUERY_DASD_TIMEOUTS 0xEB #define SET_DASD_TIMEOUTS 0xEC #define IPR_MIGRATE_ARRAY_PROTECTION 0xEF #define IPR_QUERY_ARRAY_CONFIG 0xF0 #define IPR_START_ARRAY_PROTECTION 0xF1 #define IPR_STOP_ARRAY_PROTECTION 0xF2 #define IPR_RESYNC_ARRAY_PROTECTION 0xF3 #define IPR_ADD_ARRAY_DEVICE 0xF4 #define IPR_REMOVE_ARRAY_DEVICE 0xF5 #define IPR_REBUILD_DEVICE_DATA 0xF6 #define IPR_RECLAIM_CACHE_STORE 0xF8 #define IPR_SET_ARRAY_ASYMMETRIC_ACCESS 0xFA #define IPR_RECLAIM_ACTION 0x68 #define IPR_RECLAIM_PERFORM 0x00 #define IPR_RECLAIM_RESET_BATTERY_ERROR 0x08 #define IPR_RECLAIM_EXTENDED_INFO 0x10 #define IPR_RECLAIM_QUERY 0x20 #define IPR_RECLAIM_RESET 0x40 #define IPR_RECLAIM_FORCE_BATTERY_ERROR 0x60 #define IPR_RECLAIM_UNKNOWN_PERM 0x80 #define SET_SUPPORTED_DEVICES 0xFB #define IPR_STD_INQUIRY 0xFF #ifndef REPORT_LUNS #define REPORT_LUNS 0xA0 #endif #define IPR_XLATE_DEV_FMT_RC(rc) ((((rc) & 127) == 51) ? -EIO : 0) #define IPR_TYPE_AF_DISK 0xC #define IPR_TYPE_ADAPTER 0x1f #define IPR_TYPE_EMPTY_SLOT 0xff #define IPR_ACTIVE_OPTIMIZED 0x0 #define IPR_ACTIVE_NON_OPTIMIZED 0x1 #define IPR_ACTIVE_STANDBY 0x2 #define IPR_CLEAR_ASYMMETRIC_STATE 0x0 #define IPR_PRESERVE_ASYMMETRIC_STATE 0x80 #define IPR_IOA_CACHING_DUAL_FAILURE_DISABLED 0x0 #define IPR_IOA_CACHING_DUAL_FAILURE_ENABLED 0x1 #define IPR_IOA_REQUESTED_CACHING_DEFAULT 0x0 #define IPR_IOA_REQUESTED_CACHING_DISABLED 0x1 #define IPR_IOA_CACHING_DEFAULT_DUAL_ENABLED 0x2 #define IPR_IOA_CACHING_DISABLED_DUAL_ENABLED 0x3 #define IPR_IOA_SET_CACHING_DEFAULT 0x0 #define IPR_IOA_SET_CACHING_DISABLED 0x10 #define IPR_IOA_SET_CACHING_DUAL_DISABLED 0x20 #define IPR_IOA_SET_CACHING_DUAL_ENABLED 0x30 #define PHYSICAL_LOCATION_LENGTH 1024 #define IPR_HDD 0x0 #define IPR_SSD 0x1 #define IPR_BLK_DEV_CLASS_4K 0x4 #define IPR_ARRAY_VIRTUAL_BUS 0x1 #define IPR_VSET_VIRTUAL_BUS 0x2 #define IPR_IOAFP_VIRTUAL_BUS 0x3 #define IPR_IS_DASD_DEVICE(std_inq_data) \ ((((std_inq_data).peri_dev_type) == TYPE_DISK) && !((std_inq_data).removeable_medium)) #define IPR_SET_MODE(change_mask, cur_val, new_val) \ { \ int mod_bits = (cur_val ^ new_val); \ if ((change_mask & mod_bits) == mod_bits) \ { \ cur_val = new_val; \ } \ } #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) #define IPR_RECORD_ID_SUPPORTED_ARRAYS __constant_be16_to_cpu((u16)0) #define IPR_RECORD_ID_COMP_RECORD __constant_be16_to_cpu((u16)3) #define IPR_RECORD_ID_ARRAY_RECORD __constant_be16_to_cpu((u16)4) #define IPR_RECORD_ID_DEVICE_RECORD __constant_be16_to_cpu((u16)5) #define IPR_RECORD_ID_ARRAY_RECORD_3 __constant_be16_to_cpu((u16)6) #define IPR_RECORD_ID_VSET_RECORD_3 __constant_be16_to_cpu((u16)7) #define IPR_RECORD_ID_DEVICE_RECORD_3 __constant_be16_to_cpu((u16)8) extern struct ipr_array_query_data *ipr_array_query_data; extern int num_ioas; extern struct ipr_ioa *ipr_ioa_head; extern struct ipr_ioa *ipr_ioa_tail; extern int runtime; extern void (*exit_func) (void); extern int daemonize; extern int ipr_debug; extern int ipr_force; extern int ipr_sg_required; extern int polling_mode; extern int ipr_fast; extern int format_done; extern char *tool_name; extern struct sysfs_dev *head_zdev; extern struct sysfs_dev *tail_zdev; struct sysfs_dev { char sysfs_device_name[PATH_MAX]; struct sysfs_dev *next, *prev; }; struct ipr_phy { #if defined (__BIG_ENDIAN_BITFIELD) u8 box:3; u8 phy:5; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 phy:5; u8 box:3; #endif }; struct ipr_phy_2bit_hop { #if defined (__BIG_ENDIAN_BITFIELD) u8 box:2; u8 phy:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 phy:6; u8 box:2; #endif }; struct ipr_res_addr { u8 host; u8 bus; union { u8 target; struct ipr_phy phy; struct ipr_phy_2bit_hop phy_2bit_hop; }; u8 lun; #define IPR_GET_PHYSICAL_LOCATOR(res_addr) \ (((res_addr)->bus << 16) | ((res_addr)->target << 8) | (res_addr)->lun) }; struct ipr_res_path { u8 res_path_bytes[8]; }; struct ipr_std_inq_vpids { u8 vendor_id[IPR_VENDOR_ID_LEN]; /* Vendor ID */ u8 product_id[IPR_PROD_ID_LEN]; /* Product ID */ }; struct ipr_common_record { u16 record_id; u16 record_len; }; struct ipr_vset_res_state { u16 stripe_size; u8 prot_level; u8 num_devices_in_vset; u32 reserved6; u32 ilid; u32 failing_dev_ioasc; struct ipr_res_addr failing_dev_res_addr; u32 failing_dev_res_handle; u8 prot_level_str[8]; }; struct ipr_dasd_res_state { u32 data_path_width; /* bits */ u32 data_xfer_rate; /* 100 KBytes/second */ u32 ilid; u32 failing_dev_ioasc; struct ipr_res_addr failing_dev_res_addr; u32 failing_dev_res_handle; }; struct ipr_gscsi_res_state { u32 data_path_width; /* bits */ u32 data_xfer_rate; /* 100 KBytes/second */ }; struct ipr_supported_device { u16 data_length; u8 reserved; u8 num_records; struct ipr_std_inq_vpids vpids; u8 reserved2[16]; }; struct ipr_mode_page_hdr { #if defined (__BIG_ENDIAN_BITFIELD) u8 parms_saveable:1; u8 reserved1:1; u8 page_code:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 page_code:6; u8 reserved1:1; u8 parms_saveable:1; #endif u8 page_length; }; struct ipr_vendor_mode_page { /* Mode page 0x00 */ struct ipr_mode_page_hdr hdr; #if defined (__BIG_ENDIAN_BITFIELD) u8 qpe:1; u8 uqe:1; u8 dwd:1; u8 reserved1:4; u8 arhes:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 arhes:1; u8 reserved1:4; u8 dwd:1; u8 uqe:1; u8 qpe:1; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 asdpe:1; u8 reserved2:1; u8 cmdac:1; u8 rpfae:1; u8 dotf:1; u8 reserved3:1; u8 rrnde:1; u8 cpe:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 cpe:1; u8 rrnde:1; u8 reserved3:1; u8 dotf:1; u8 rpfae:1; u8 cmdac:1; u8 reserved2:1; u8 asdpe:1; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved4:6; u8 dwlro:1; u8 dlro:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 dlro:1; u8 dwlro:1; u8 reserved4:6; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved5:2; u8 dsn:1; u8 frdd:1; u8 dpsdp:1; u8 wpen:1; u8 caen:1; u8 ovple:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 ovple:1; u8 caen:1; u8 wpen:1; u8 dpsdp:1; u8 frdd:1; u8 dsn:1; u8 reserved5:2; #endif u8 reserved7[2]; #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved8:1; u8 adc:1; u8 qemc:1; u8 drd:1; u8 led_mode:4; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 led_mode:4; u8 drd:1; u8 qemc:1; u8 adc:1; u8 reserved8:1; #endif u8 temp_threshold; u8 cmd_aging_limit_hi; u8 cmd_aging_limit_lo; u8 qpe_read_threshold; u8 reserved10; #if defined (__BIG_ENDIAN_BITFIELD) u8 drrt:1; u8 dnr:1; u8 reserved11:1; u8 rarr:1; u8 ffmt:1; u8 reserved12:3; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved12:3; u8 ffmt:1; u8 rarr:1; u8 reserved11:1; u8 dnr:1; u8 drrt:1; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 rtp:1; u8 rrc:1; u8 fcert:1; u8 reserved13:1; u8 drpdv:1; u8 dsf:1; u8 irt:1; u8 ivr:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 ivr:1; u8 irt:1; u8 dsf:1; u8 drpdv:1; u8 reserved13:1; u8 fcert:1; u8 rrc:1; u8 rtp:1; #endif }; struct ipr_control_mode_page { /* Mode page 0x0A */ struct ipr_mode_page_hdr hdr; #if defined (__BIG_ENDIAN_BITFIELD) u8 tst:3; u8 reserved1:3; u8 gltsd:1; u8 rlec:1; u8 queue_algorithm_modifier:4; u8 reserved2:1; u8 qerr:2; u8 dque:1; u8 reserved3:1; u8 rac:1; u8 reserved4:2; u8 swp:1; u8 raerp:1; u8 uaaerp:1; u8 eaerp:1; u8 ato:1; u8 tas:1; u8 reserved5:3; u8 autoload_mode:3; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 rlec:1; u8 gltsd:1; u8 reserved1:3; u8 tst:3; u8 dque:1; u8 qerr:2; u8 reserved2:1; u8 queue_algorithm_modifier:4; u8 eaerp:1; u8 uaaerp:1; u8 raerp:1; u8 swp:1; u8 reserved4:2; u8 rac:1; u8 reserved3:1; u8 autoload_mode:3; u8 reserved5:3; u8 tas:1; u8 ato:1; #endif u16 ready_aen_holdoff_period; u16 busy_timeout_period; u16 reserved6; }; struct ipr_rw_err_mode_page { /* Page code 0x01 */ struct ipr_mode_page_hdr hdr; #if defined (__BIG_ENDIAN_BITFIELD) u8 awre:1; u8 arre:1; u8 tb:1; u8 rc:1; u8 eer:1; u8 per:1; u8 dte:1; u8 dcr:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 dcr:1; u8 dte:1; u8 per:1; u8 eer:1; u8 rc:1; u8 tb:1; u8 arre:1; u8 awre:1; #endif u8 read_retry_count; u8 correction_span; u8 head_offset_count; u8 data_strobe_offset_count; u8 reserved1; u8 write_retry_count; u8 reserved2; u16 recovery_time_limit; }; struct ipr_reclaim_query_data { u8 action_status; #define IPR_ACTION_SUCCESSFUL 0 #define IPR_ACTION_NOT_REQUIRED 1 #define IPR_ACTION_NOT_PERFORMED 2 #if defined (__BIG_ENDIAN_BITFIELD) u8 reclaim_known_needed:1; u8 reclaim_unknown_needed:1; u8 reserved2:2; u8 reclaim_known_performed:1; u8 reclaim_unknown_performed:1; u8 reserved3:1; u8 num_blocks_needs_multiplier:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 num_blocks_needs_multiplier:1; u8 reserved3:1; u8 reclaim_unknown_performed:1; u8 reclaim_known_performed:1; u8 reserved2:2; u8 reclaim_unknown_needed:1; u8 reclaim_known_needed:1; #endif u16 num_blocks; u8 rechargeable_battery_type; #define IPR_BATTERY_TYPE_NO_BATTERY 0 #define IPR_BATTERY_TYPE_NICD 1 #define IPR_BATTERY_TYPE_NIMH 2 #define IPR_BATTERY_TYPE_LIION 3 u8 rechargeable_battery_error_state; #define IPR_BATTERY_NO_ERROR_STATE 0 #define IPR_BATTERY_WARNING_STATE 1 #define IPR_BATTERY_ERROR_STATE 2 #if defined (__BIG_ENDIAN_BITFIELD) u8 conc_maint_battery:1; u8 battery_replace_allowed:1; u8 reserved4:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved4:6; u8 battery_replace_allowed:1; u8 conc_maint_battery:1; #endif u8 reserved5; u16 raw_power_on_time; u16 adjusted_power_on_time; u16 estimated_time_to_battery_warning; u16 estimated_time_to_battery_failure; u8 reserved6[240]; }; struct ipr_sdt_entry { u32 bar_str_offset; u32 end_offset; u8 entry_byte; u8 reserved[3]; #if defined (__BIG_ENDIAN_BITFIELD) u8 endian:1; u8 reserved1:1; u8 valid_entry:1; u8 reserved2:5; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved2:5; u8 valid_entry:1; u8 reserved1:1; u8 endian:1; #endif u8 resv; u16 priority; }; struct ipr_query_res_state { #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved1:1; u8 not_oper:1; u8 not_ready:1; u8 not_func:1; u8 reserved2:4; u8 read_write_prot:1; u8 reserved3:7; u8 prot_dev_failed:1; u8 prot_suspended:1; u8 prot_resuming:1; u8 degraded_oper:1; u8 service_req:1; u8 reserved4:3; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved2:4; u8 not_func:1; u8 not_ready:1; u8 not_oper:1; u8 reserved1:1; u8 reserved3:7; u8 read_write_prot:1; u8 reserved4:3; u8 service_req:1; u8 degraded_oper:1; u8 prot_resuming:1; u8 prot_suspended:1; u8 prot_dev_failed:1; #endif u8 reserved5; union { struct ipr_vset_res_state vset; struct ipr_dasd_res_state dasd; struct ipr_gscsi_res_state gscsi; }; }; struct ipr_query_io_port { u32 length; #define IOA_DEV_PORT_ACTIVE 0x0 #define IOA_DEV_PORT_SUSPEND 0x1 #define IOA_DEV_PORT_PARTIAL_SUSPEND 0x2 #define IOA_DEV_PORT_UNKNOWN 0xFF u8 port_state; u8 reserved1; u8 reserved2; u8 reserved3; }; struct ipr_res_addr_aliases { u32 length; struct ipr_res_addr res_addr[10]; }; #define for_each_ra_alias(ra, aliases) \ for (ra = (aliases)->res_addr; \ ra < ((aliases)->res_addr + (ntohl((aliases)->length) / sizeof(struct ipr_res_addr))) && \ ra < ((aliases)->res_addr + ARRAY_SIZE((aliases)->res_addr)); \ ra++) struct ipr_res_path_aliases { u32 length; u32 reserved1; struct ipr_res_path res_path[10]; }; #define for_each_rp_alias(rp, aliases) \ for (rp = (aliases)->res_path; \ rp < ((aliases)->res_path + ((ntohl((aliases)->length)- 4) / sizeof(struct ipr_res_path))) && \ rp < ((aliases)->res_path + ARRAY_SIZE((aliases)->res_path)); \ rp++) struct ipr_array_cap_entry { u8 prot_level; #define IPR_DEFAULT_RAID_LVL "5" #if defined (__BIG_ENDIAN_BITFIELD) u8 include_allowed:1; u8 reserved:5; u8 format_overlay_type:2; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 format_overlay_type:2; u8 reserved:5; u8 include_allowed:1; #endif #define IPR_FORMAT_ADD_DEVICES 1 #define IPR_FORMAT_REMOVE_DEVICES 2 u16 reserved2; u8 reserved3; u8 max_num_array_devices; u8 min_num_array_devices; u8 min_mult_array_devices; u16 reserved4; u16 supported_stripe_sizes; u16 reserved5; u16 recommended_stripe_size; u8 prot_level_str[8]; }; #define for_each_cap_entry(cap, supp) \ for (cap = (supp)->entry; \ (unsigned long)cap < ((unsigned long)((supp)->entry) + (ntohs((supp)->num_entries) * ntohs((supp)->entry_length))); \ cap = (struct ipr_array_cap_entry *)((unsigned long)cap + ntohs((supp)->entry_length))) struct ipr_array_record { struct ipr_common_record common; #if defined (__BIG_ENDIAN_BITFIELD) u8 issue_cmd:1; u8 known_zeroed:1; u8 reserved1:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved1:6; u8 known_zeroed:1; u8 issue_cmd:1; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 saved_asym_access_state:4; u8 current_asym_access_state:4; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 current_asym_access_state:4; u8 saved_asym_access_state:4; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 established:1; u8 exposed:1; u8 non_func:1; u8 high_avail:1; u8 no_config_entry:1; u8 reserved3:3; u8 start_cand:1; u8 stop_cand:1; u8 resync_cand:1; u8 migrate_cand:1; u8 asym_access_cand:1; u8 reserved4:3; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved3:3; u8 no_config_entry:1; u8 high_avail:1; u8 non_func:1; u8 exposed:1; u8 established:1; u8 reserved4:3; u8 asym_access_cand:1; u8 migrate_cand:1; u8 resync_cand:1; u8 stop_cand:1; u8 start_cand:1; #endif union { struct { u16 stripe_size; u8 raid_level; u8 array_id; u32 resource_handle; struct ipr_res_addr resource_addr; struct ipr_res_addr last_resource_addr; u8 vendor_id[IPR_VENDOR_ID_LEN]; u8 product_id[IPR_PROD_ID_LEN]; u8 serial_number[8]; #if defined (__BIG_ENDIAN_BITFIELD) u8 block_dev_class:3; u8 reserved5:5; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved5:5; u8 block_dev_class:3; #endif u8 reserved6; u8 reserved7; u8 reserved8; }__attribute__((packed, aligned (4))) type2; struct { u8 reserved5; u8 raid_level; u16 stripe_size; u8 reserved6[4]; u8 reserved7[16]; u8 last_func_res_path[8]; u8 reserved8[2]; u8 array_id; #if defined (__BIG_ENDIAN_BITFIELD) u8 block_dev_class:3; u8 reserved9:5; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved9:5; u8 block_dev_class:3; #endif u32 resource_handle; u8 dev_id[8]; u8 lun[8]; u8 wwn[16]; u8 res_path[8]; u8 vendor_id[IPR_VENDOR_ID_LEN]; u8 product_id[IPR_PROD_ID_LEN]; u8 serial_number[IPR_SERIAL_NUM_LEN]; u8 desc[IPR_DESCRIPTION_LEN]; u8 total_arr_size[8]; u8 total_size_inuse[8]; u8 max_size_to_use[8]; u8 total_size_enc[8]; u8 total_inuse_enc[8]; u8 max_size_enc[8]; }__attribute__((packed, aligned (4))) type3; }; }__attribute__((packed, aligned (4))); struct ipr_dev_record { struct ipr_common_record common; #if defined (__BIG_ENDIAN_BITFIELD) u8 issue_cmd:1; u8 known_zeroed:1; u8 reserved:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved:6; u8 known_zeroed:1; u8 issue_cmd:1; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 saved_asym_access_state:4; u8 current_asym_access_state:4; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 current_asym_access_state:4; u8 saved_asym_access_state:4; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 array_member:1; u8 has_parity:1; u8 is_exposed_device:1; u8 is_hot_spare:1; u8 no_cfgte_vol:1; u8 no_cfgte_dev:1; u8 reserved2:2; u8 start_cand:1; u8 parity_cand:1; u8 stop_cand:1; u8 resync_cand:1; u8 include_cand:1; u8 exclude_cand:1; u8 rebuild_cand:1; u8 zero_cand:1; u8 add_hot_spare_cand:1; u8 rmv_hot_spare_cand:1; u8 migrate_array_prot_cand:1; u8 reserved3:5; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved2:2; u8 no_cfgte_dev:1; u8 no_cfgte_vol:1; u8 is_hot_spare:1; u8 is_exposed_device:1; u8 has_parity:1; u8 array_member:1; u8 zero_cand:1; u8 rebuild_cand:1; u8 exclude_cand:1; u8 include_cand:1; u8 resync_cand:1; u8 stop_cand:1; u8 parity_cand:1; u8 start_cand:1; u8 reserved3:5; u8 migrate_array_prot_cand:1; u8 rmv_hot_spare_cand:1; u8 add_hot_spare_cand:1; #endif union { struct { u8 reserved4[2]; u8 array_id; u32 resource_handle; struct ipr_res_addr resource_addr; struct ipr_res_addr last_resource_addr; u8 vendor_id[IPR_VENDOR_ID_LEN]; u8 product_id[IPR_PROD_ID_LEN]; u8 serial_number[IPR_SERIAL_NUM_LEN]; #if defined (__BIG_ENDIAN_BITFIELD) u8 block_dev_class:3; u8 reserved5:5; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved5:5; u8 block_dev_class:3; #endif u8 reserved6; u8 reserved7; u8 reserved8; }__attribute__((packed, aligned (4))) type2; struct { u8 reserved4[3]; u8 reserved5[4]; u8 reserved6[16]; u8 last_func_res_path[8]; u8 reserved7[2]; u8 array_id; #if defined (__BIG_ENDIAN_BITFIELD) u8 block_dev_class:3; u8 reserved8:5; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved8:5; u8 block_dev_class:3; #endif u32 resource_handle; u8 dev_id[8]; u8 lun[8]; u8 wwn[16]; u8 res_path[8]; u8 vendor_id[IPR_VENDOR_ID_LEN]; u8 product_id[IPR_PROD_ID_LEN]; u8 serial_number[IPR_SERIAL_NUM_LEN]; u8 desc[IPR_DESCRIPTION_LEN]; u8 reserved9[8]; u8 reserved10[2]; u8 reserved11[6]; u8 reserved12[8]; }__attribute__((packed, aligned (4))) type3; }; }__attribute__((packed, aligned (4))); #define __for_each_qac_entry(rcd, qac, type) \ if ((qac)->num_records) \ for (rcd = (type *)(qac)->data; \ ((unsigned long)rcd) < ((unsigned long)((qac)->u.buf + (qac)->resp_len)) && \ ((unsigned long)rcd) < ((unsigned long)((qac)->u.buf + IPR_QAC_BUFFER_SIZE)); \ rcd = (type *)((unsigned long)rcd + ntohs(((struct ipr_common_record *)rcd)->record_len))) #define for_each_supported_arrays_rcd(rcd, qac) \ __for_each_qac_entry(rcd, qac, struct ipr_supported_arrays) \ if (rcd->common.record_id == IPR_RECORD_ID_SUPPORTED_ARRAYS) #define for_each_qac_entry(rcd, qac) \ __for_each_qac_entry(rcd, qac, struct ipr_common_record) #define for_each_dev_rcd(rcd, qac) \ __for_each_qac_entry(rcd, qac, struct ipr_dev_record) \ if (ipr_is_device_record(rcd->common.record_id)) #define for_each_array_rcd(rcd, qac) \ __for_each_qac_entry(rcd, qac, struct ipr_array_record) \ if (ipr_is_array_record(rcd->common.record_id)) struct ipr_std_inq_data { #if defined (__BIG_ENDIAN_BITFIELD) u8 peri_qual:3; u8 peri_dev_type:5; u8 removeable_medium:1; u8 reserved1:7; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 peri_dev_type:5; u8 peri_qual:3; u8 reserved1:7; u8 removeable_medium:1; #endif u8 version; #if defined (__BIG_ENDIAN_BITFIELD) u8 aen:1; u8 obsolete1:1; u8 norm_aca:1; u8 hi_sup:1; u8 resp_data_fmt:4; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 resp_data_fmt:4; u8 hi_sup:1; u8 norm_aca:1; u8 obsolete1:1; u8 aen:1; #endif u8 additional_len; #if defined (__BIG_ENDIAN_BITFIELD) u8 sccs:1; u8 reserved2:7; u8 bque:1; u8 enc_serv:1; u8 vs:1; u8 multi_port:1; u8 mchngr:1; u8 obsolete2:2; u8 addr16:1; u8 rel_adr:1; u8 obsolete3:1; u8 wbus16:1; u8 sync:1; u8 linked:1; u8 trans_dis:1; u8 cmd_que:1; u8 vs2:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved2:7; u8 sccs:1; u8 addr16:1; u8 obsolete2:2; u8 mchngr:1; u8 multi_port:1; u8 vs:1; u8 enc_serv:1; u8 bque:1; u8 vs2:1; u8 cmd_que:1; u8 trans_dis:1; u8 linked:1; u8 sync:1; u8 wbus16:1; u8 obsolete3:1; u8 rel_adr:1; #endif struct ipr_std_inq_vpids vpids; u8 ros_rsvd_ram_rsvd[4]; u8 serial_num[IPR_SERIAL_NUM_LEN]; }; struct ipr_std_inq_data_long { struct ipr_std_inq_data std_inq_data; u8 z1_term[IPR_STD_INQ_Z1_TERM_LEN]; #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved:4; u8 clocking:2; u8 qas:1; u8 ius:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 ius:1; u8 qas:1; u8 clocking:2; u8 reserved:4; #endif u8 reserved1[41]; u8 z2_term[IPR_STD_INQ_Z2_TERM_LEN]; u8 z3_term[IPR_STD_INQ_Z3_TERM_LEN]; u8 as400_service_level; u8 z4_term[IPR_STD_INQ_Z4_TERM_LEN]; u8 z5_term[IPR_STD_INQ_Z5_TERM_LEN]; u8 part_number[IPR_STD_INQ_PART_NUM_LEN]; u8 ec_level[IPR_STD_INQ_EC_LEVEL_LEN]; u8 fru_number[IPR_STD_INQ_FRU_NUM_LEN]; u8 z6_term[IPR_STD_INQ_Z6_TERM_LEN]; }; struct ipr_mode_page_28_scsi_dev_bus_attr { struct ipr_res_addr res_addr; #if defined (__BIG_ENDIAN_BITFIELD) u8 qas_capability:2; u8 enable_target_mode:1; u8 term_power_absent:1; u8 target_mode_supported:1; u8 lvd_to_se_transition_not_allowed:1; u8 reserved2:2; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved2:2; u8 lvd_to_se_transition_not_allowed:1; u8 target_mode_supported:1; u8 term_power_absent:1; u8 enable_target_mode:1; u8 qas_capability:2; #endif #define IPR_MODEPAGE28_QAS_CAPABILITY_NO_CHANGE 0 #define IPR_MODEPAGE28_QAS_CAPABILITY_DISABLE_ALL 1 #define IPR_MODEPAGE28_QAS_CAPABILITY_ENABLE_ALL 2 /* NOTE: Due to current operation conditions QAS should never be enabled so the change mask will be set to 0 */ #define IPR_MODEPAGE28_QAS_CAPABILITY_CHANGE_MASK 0 u8 scsi_id; #define IPR_MODEPAGE28_SCSI_ID_NO_CHANGE 0x80u #define IPR_MODEPAGE28_SCSI_ID_NO_ID 0xFFu u8 bus_width; #define IPR_MODEPAGE28_BUS_WIDTH_NO_CHANGE 0 u8 extended_reset_delay; #define IPR_EXTENDED_RESET_DELAY 7 u32 max_xfer_rate; #define IPR_MODEPAGE28_MAX_XFR_RATE_NO_CHANGE 0 u8 min_time_delay; #define IPR_DEFAULT_SPINUP_DELAY 0xFFu #define IPR_INIT_SPINUP_DELAY 5 u8 reserved3; u16 reserved4; }; #define IPR_BUS_XFER_RATE_TO_THRUPUT(rate, width) \ (((rate) * ((width) / 8)) / 10) #define IPR_BUS_THRUPUT_TO_XFER_RATE(thruput, width) \ (((thruput) * 10) / ((width) / 8)) #define IPR_CONFIG_DIR "/etc/ipr/" #define IPR_CATEGORY_IOA "Adapter" #define IPR_GSCSI_HA_ONLY "JBOD_ONLY_HA" #define IPR_DUAL_ADAPTER_ACTIVE_ACTIVE "DUAL_ADAPTER_ACTIVE_ACTIVE" #define IPR_CATEGORY_BUS "Bus" #define IPR_QAS_CAPABILITY "QAS_CAPABILITY" #define IPR_HOST_SCSI_ID "HOST_SCSI_ID" #define IPR_BUS_WIDTH "BUS_WIDTH" #define IPR_MAX_XFER_RATE_STR "MAX_BUS_SPEED" #define IPR_MIN_TIME_DELAY "MIN_TIME_DELAY" #define IPR_CATEGORY_DISK "Disk" #define IPR_QUEUE_DEPTH "QUEUE_DEPTH" #define IPR_TCQ_ENABLED "TCQ_ENABLED" #define IPR_FORMAT_TIMEOUT "FORMAT_UNIT_TIMEOUT" #define IPR_MAX_XFER_RATE 320 #define IPR_SAFE_XFER_RATE 160 /* Internal return codes */ #define RC_SUCCESS 0 #define RC_FAILED -1 #define RC_NOOP -2 #define RC_NOT_PERFORMED -3 #define RC_IN_USE -4 #define RC_CANCEL 12 #define RC_REFRESH 1 #define RC_EXIT 3 struct scsi_dev_data { int host; int channel; union { u8 id; struct ipr_phy phy; struct ipr_phy_2bit_hop phy_2bit_hop; }; int lun; int type; int opens; int qdepth; int busy; int online; u32 handle; char vendor_id[IPR_VENDOR_ID_LEN + 1]; char product_id[IPR_PROD_ID_LEN + 1]; char sysfs_device_name[PATH_MAX]; char dev_name[64]; char gen_name[64]; #define IPR_MAX_RES_PATH_LEN 24 char res_path[IPR_MAX_RES_PATH_LEN]; u64 device_id; }; struct ipr_path_entry { u8 state; #define IPR_PATH_FUNCTIONAL 0 #define IPR_PATH_NOT_FUNCTIONAL 1 u8 local_bus_num; u8 remote_bus_num; u8 reserved; u8 local_connection_id[8]; u8 remote_connection_id[8]; }; struct ipr_path_entries { u32 num_path_entries; struct ipr_path_entry path[0]; /* variable length */ }; struct ipr_dual_ioa_entry { u32 length; u8 link_state; #define IPR_IOA_LINK_STATE_NOT_OPER 0 #define IPR_IOA_LINK_STATE_OPER 1 u8 cur_state; #define IPR_IOA_STATE_NO_CHANGE 0 #define IPR_IOA_STATE_PRIMARY 2 #define IPR_IOA_STATE_SECONDARY 3 #define IPR_IOA_STATE_NO_PREFERENCE 4 u8 fmt; u8 multi_adapter_type; #define IPR_IOA_MA_TYPE_UNDEFINED 0 #define IPR_IOA_MA_TYPE_DUAL_IOA 1 #define IPR_IOA_MA_TYPE_AUX_CACHE 2 u8 reserved[2]; u16 add_len; union { struct { u8 remote_vendor_id[IPR_VENDOR_ID_LEN]; u8 remote_prod_id[IPR_PROD_ID_LEN]; u8 remote_sn[IPR_SERIAL_NUM_LEN]; } fmt0; struct { u8 remote_vendor_id[IPR_VENDOR_ID_LEN]; u8 remote_prod_id[IPR_PROD_ID_LEN]; u8 remote_sn[IPR_SERIAL_NUM_LEN]; u8 wwid[8]; } fmt1; }; }; struct ipr_phys_bus_entry { u8 bus_num; u8 reserved[3]; u8 conn_id[8]; }; struct ipr_ioa_cap_entry { u32 length; u8 reserved; u8 preferred_role; u8 token; u8 reserved2[4]; u8 num_bus_entries; struct ipr_phys_bus_entry bus[0]; }; struct ipr_multi_ioa_status { u32 length; u32 num_entries; struct ipr_ioa_cap_entry cap; u8 buf[16 * 1024]; /* * struct ipr_dual_ioa_entry ioa[0]; */ }; struct ipr_disk_attr { int queue_depth; int tcq_enabled; int format_timeout; }; struct ipr_vset_attr { int queue_depth; }; struct ipr_ioa_attr { int preferred_primary; int gscsi_only_ha; int active_active; int caching; }; #define IPR_DEV_MAX_PATHS 2 struct ipr_dev { char dev_name[64]; char gen_name[64]; char prot_level_str[8]; u8 *vendor_id; u8 *product_id; u8 *serial_number; u8 array_id; u8 raid_level; u16 stripe_size; u32 resource_handle; u8 block_dev_class; u32 is_reclaim_cand:1; u32 should_init:1; u32 init_not_allowed:1; u32 local_flag:1; u32 rescan:1; u8 active_suspend; u32 is_suspend_cand:1; u32 is_resume_cand:1; struct scsi_dev_data *scsi_dev_data; struct ipr_dev *ses[IPR_DEV_MAX_PATHS]; struct ipr_res_addr res_addr[IPR_DEV_MAX_PATHS]; struct ipr_res_path res_path[IPR_DEV_MAX_PATHS]; char res_path_name[IPR_MAX_RES_PATH_LEN]; struct ipr_disk_attr attr; char physical_location[PHYSICAL_LOCATION_LENGTH]; union { struct ipr_common_record *qac_entry; struct ipr_dev_record *dev_rcd; struct ipr_array_record *array_rcd; }; struct ipr_dev *alt_path; struct ipr_ioa *ioa; }; #define for_each_ra(ra, dev) \ for(ra = (dev)->res_addr; \ (ra - ((dev)->res_addr)) < IPR_DEV_MAX_PATHS; ra++) #define for_each_rp(rp, dev) \ for(rp = (dev)->res_path; \ (rp - ((dev)->res_path)) < IPR_DEV_MAX_PATHS; rp++) enum ipr_tcq_mode { IPR_TCQ_DISABLE = 0, IPR_TCQ_FROZEN = 1, IPR_TCQ_NACA = 2 }; #define IPR_MAX_IOA_DEVICES (IPR_MAX_NUM_BUSES * 45 * 2 + 1) struct ipr_ioa { struct ipr_dev ioa; u16 ccin; u16 af_block_size; u32 host_addr; u32 num_raid_cmds; u32 msl; u16 num_devices; u8 ioa_dead:1; u8 nr_ioa_microcode:1; u8 scsi_id_changeable:1; u8 dual_raid_support:1; u8 is_secondary:1; u8 should_init:1; u8 is_aux_cache:1; u8 protect_last_bus:1; u8 gscsi_only_ha:1; u8 in_gscsi_only_ha:1; u8 asymmetric_access:1; u8 asymmetric_access_enabled:1; u8 has_cache:1; u8 sis64:1; #define IPR_SIS32 0x00 #define IPR_SIS64 0x01 u8 support_4k:1; enum ipr_tcq_mode tcq_mode; u16 pci_vendor; u16 pci_device; u16 subsystem_vendor; u16 subsystem_device; u16 hop_count; #define IPR_NOHOP 0x00 #define IPR_2BIT_HOP 0x01 #define IPR_3BIT_HOP 0x02 char dual_state[16]; char preferred_dual_state[16]; int host_num; char pci_address[16]; char host_name[16]; char physical_location[1024]; u8 yl_serial_num[YL_SERIAL_NUM_LEN]; struct ipr_dev dev[IPR_MAX_IOA_DEVICES]; struct ipr_multi_ioa_status ioa_status; struct ipr_array_query_data *qac_data; struct ipr_array_record *start_array_qac_entry; struct ipr_supported_arrays *supported_arrays; struct ipr_reclaim_query_data *reclaim_data; struct ipr_ioa *next; struct ipr_ioa *cmd_next; }; #define __for_each_ioa(ioa, head) for (ioa = head; ioa; ioa = ioa->next) #define for_each_ioa(ioa) __for_each_ioa(ioa, ipr_ioa_head) #define for_each_primary_ioa(ioa) \ __for_each_ioa(ioa, ipr_ioa_head) \ if (!ioa->is_secondary) #define for_each_secondary_ioa(ioa) \ __for_each_ioa(ioa, ipr_ioa_head) \ if (ioa->is_secondary) #define for_each_sas_ioa(ioa) \ __for_each_ioa(ioa, ipr_ioa_head) \ if (!__ioa_is_spi(ioa)) #define for_each_dev(i, d) for (d = (i)->dev; (d - (i)->dev) < (i)->num_devices; d++) #define for_each_hotplug_dev(i, d) \ for_each_dev(i, d) \ if (ipr_is_af_dasd_device(d) || ipr_is_gscsi(d)) #define for_each_af_dasd(i, d) \ for_each_dev(i, d) \ if (ipr_is_af_dasd_device(d)) #define for_each_dev_in_vset(v, d) \ for_each_af_dasd((v)->ioa, d) \ if (ipr_is_array_member(d) && d->array_id == (v)->array_id) #define for_each_vset(i, d) \ for_each_dev(i, d) \ if (ipr_is_volume_set(d) && !d->array_rcd->start_cand) #define for_each_array(i, d) \ for_each_dev(i, d) \ if (ipr_is_array(d) && !d->array_rcd->start_cand) #define for_each_ses(i, d) \ for_each_dev(i, d) \ if (d->scsi_dev_data && d->scsi_dev_data->type == TYPE_ENCLOSURE) #define for_each_hot_spare(i, d) \ for_each_dev(i, d) \ if (ipr_is_hot_spare(d)) #define for_each_disk(i, d) \ for_each_dev(i, d) \ if ((d)->scsi_dev_data && (ipr_is_af_dasd_device(d) || ipr_is_gscsi(d))) #define __for_each_disk(i, d) \ for_each_dev(i, d) \ if ((d)->scsi_dev_data && ((d)->scsi_dev_data->type == TYPE_DISK || \ (d)->scsi_dev_data->type == IPR_TYPE_AF_DISK)) #define for_each_standalone_disk(i, d) \ for_each_disk(i, d) \ if (!ipr_is_hot_spare(d) && !ipr_is_array_member(d)) #define __for_each_standalone_disk(i, d) \ __for_each_disk(i, d) \ if (!ipr_is_hot_spare(d) && !ipr_is_volume_set(d) && \ !ipr_is_array_member(d)) #define for_each_standalone_af_disk(i, d) \ for_each_standalone_disk(i, d) \ if (ipr_is_af_dasd_device(d)) #define for_each_jbod_disk(i, d) \ for_each_disk(i, d) \ if (ipr_is_gscsi(d)) struct ipr_dasd_inquiry_page3 { u8 peri_qual_dev_type; u8 page_code; u8 reserved1; u8 page_length; u8 ascii_len; u8 reserved2[3]; u8 load_id[4]; u8 release_level[4]; u8 ptf_number[4]; u8 patch_number[4]; }; struct ipr_array_query32_data { u16 resp_len; u8 reserved; u8 num_records; u8 data[0]; }; struct ipr_array_query64_data { u32 resp_len; u32 num_records; u8 data[0]; }; struct ipr_array_query_data { u32 resp_len; u32 num_records; u32 hdr_len; u8 *data; union { u8 buf[IPR_QAC_BUFFER_SIZE]; struct ipr_array_query32_data buf32; struct ipr_array_query64_data buf64; } u; }; struct ipr_block_desc { u8 num_blocks[4]; u8 density_code; u8 block_length[3]; }; struct ipr_mode_parm_hdr { u8 length; u8 medium_type; u8 device_spec_parms; u8 block_desc_len; }; struct ipr_mode_pages { struct ipr_mode_parm_hdr hdr; u8 data[255 - sizeof(struct ipr_mode_parm_hdr)]; }; struct sense_data_t { u8 error_code; u8 segment_numb; u8 sense_key; u8 info[4]; u8 add_sense_len; u8 cmd_spec_info[4]; u8 add_sense_code; u8 add_sense_code_qual; u8 field_rep_unit_code; u8 sense_key_spec[3]; u8 add_sense_bytes[0]; }; struct ipr_ioa_mode_page { struct ipr_mode_page_hdr hdr; u8 reserved; u8 max_tcq_depth; #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved1:1; u8 format_completed:1; u8 reserved2:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved2:6; u8 format_completed:1; u8 reserved1:1; #endif }; struct ipr_mode_page24 { struct ipr_mode_page_hdr hdr; #if defined (__BIG_ENDIAN_BITFIELD) u8 dual_adapter_af:2; u8 reserved:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved:6; u8 dual_adapter_af:2; #endif #define DISABLE_DUAL_IOA_AF 0x00 #define ENABLE_DUAL_IOA_AF 0x02 #define ENABLE_DUAL_IOA_ACTIVE_ACTIVE 0x03 }; struct ipr_config_term_hdr { u8 term_id; u8 len; }; struct ipr_subsys_config_term { struct ipr_config_term_hdr hdr; #define IPR_SUBSYS_CONFIG_TERM_ID 0x02 u8 config; #define IPR_AFDASD_SUBSYS 0x00 #define IPR_GSCSI_ONLY_HA_SUBSYS 0x01 u8 reserved; }; struct ipr_mode_page25 { struct ipr_mode_page_hdr hdr; u8 reserved[2]; struct ipr_config_term_hdr term; u8 data[256]; }; #define for_each_page25_term(hdr, page) \ for (hdr = &(page)->term; \ (char *)hdr < ((char *)(page) + (page)->hdr.page_length + sizeof((page)->hdr)); \ hdr = (struct ipr_config_term_hdr *)((char *)hdr + (hdr)->len + sizeof(*hdr))) struct ipr_mode_page_28_header { struct ipr_mode_page_hdr header; u8 num_dev_entries; u8 dev_entry_length; }; struct ipr_mode_page_28 { struct ipr_mode_page_28_header page_hdr; struct ipr_mode_page_28_scsi_dev_bus_attr attr[0]; }; #define for_each_bus_attr(ptr, page28, i) \ for (i = 0, ptr = page28->attr; i < page_28->page_hdr.num_dev_entries; \ i++, ptr = (struct ipr_mode_page_28_scsi_dev_bus_attr *) \ ((u8 *)ptr + page_28->page_hdr.dev_entry_length)) struct ipr_scsi_bus_attr { u32 max_xfer_rate; u8 qas_capability; u8 scsi_id; u8 bus_width; u8 extended_reset_delay; u8 min_time_delay; }; struct ipr_scsi_buses { int num_buses; struct ipr_scsi_bus_attr bus[IPR_MAX_NUM_BUSES]; }; struct ipr_dasd_timeout_record { u8 op_code; u8 reserved; u16 timeout; }; struct ipr_query_dasd_timeouts { u32 length; struct ipr_dasd_timeout_record record[100]; }; struct ipr_supported_arrays { struct ipr_common_record common; u16 num_entries; u16 entry_length; struct ipr_array_cap_entry entry[0]; }; struct ipr_read_cap { u32 max_user_lba; u32 block_length; }; struct ipr_read_cap16 { u32 max_user_lba_hi; u32 max_user_lba_lo; u32 block_length; }; /* Struct for disks that are unsupported or require a minimum microcode level prior to formatting to 522-byte sectors. */ struct unsupported_af_dasd { char vendor_id[IPR_VENDOR_ID_LEN + 1]; char compare_vendor_id_byte[IPR_VENDOR_ID_LEN]; char product_id[IPR_PROD_ID_LEN + 1]; char compare_product_id_byte[IPR_PROD_ID_LEN]; char lid[5]; /* Load ID - Bytes 8-11 of Inquiry Page 3 */ char lid_mask[4]; /* Mask for certain bytes of the LID */ uint supported_with_min_ucode_level; char min_ucode_level[5]; char min_ucode_mask[4]; /* used to mask don't cares in the ucode level */ }; struct unsupported_dasd { char vendor_id[IPR_VENDOR_ID_LEN + 1]; char compare_vendor_id_byte[IPR_VENDOR_ID_LEN]; char product_id[IPR_PROD_ID_LEN + 1]; char compare_product_id_byte[IPR_PROD_ID_LEN]; }; struct ipr_cmd_status_record { u16 reserved1; u16 length; u8 array_id; u8 command_code; u8 status; #define IPR_CMD_STATUS_SUCCESSFUL 0 #define IPR_CMD_STATUS_IN_PROGRESS 2 #define IPR_CMD_STATUS_ATTRIB_CHANGE 3 #define IPR_CMD_STATUS_FAILED 4 #define IPR_CMD_STATUS_INSUFF_DATA_MOVED 5 #define IPR_CMD_STATUS_MIXED_BLK_DEV_CLASESS 6 #define IPR_CMD_STATUS_MIXED_LOG_BLK_SIZE 7 u8 percent_complete; struct ipr_res_addr failing_dev_res_addr; u32 failing_dev_res_handle; u32 failing_dev_ioasc; u32 ilid; u32 resource_handle; u8 vset_id; u8 flags; u16 reserved; u8 failing_dev_res_path[8]; }; struct ipr_cmd_status { u16 resp_len; u16 num_records; struct ipr_cmd_status_record record[250]; }; #define for_each_cmd_status(r, s) for (r = (s)->record; (unsigned long)(r) < ((unsigned long)((s)->record) + ntohs((s)->resp_len) - 4); r = (void *)(r) + ntohs((r)->length)) struct ipr_inquiry_page0 { u8 peri_qual_dev_type; u8 page_code; u8 reserved1; u8 page_length; u8 supported_page_codes[IPR_MAX_NUM_SUPP_INQ_PAGES]; }; struct ipr_inquiry_page3 { u8 peri_qual_dev_type; u8 page_code; u8 reserved1; u8 page_length; u8 ascii_len; u8 reserved2[3]; u8 load_id[4]; u8 major_release; u8 card_type; u8 minor_release[2]; u8 ptf_number[4]; u8 patch_number[4]; }; struct ipr_inquiry_ioa_cap { u8 peri_qual_dev_type; u8 page_code; u8 reserved1; u8 page_length; u8 ascii_len; u8 reserved2; u8 sis_version[2]; #if defined (__BIG_ENDIAN_BITFIELD) u8 dual_ioa_raid:1; u8 dual_ioa_wcache:1; u8 dual_ioa_asymmetric_access:1; u8 reserved:5; u8 can_attach_to_aux_cache:1; u8 is_aux_cache:1; u8 is_dual_wide:1; u8 gscsi_only_ha:1; u8 reserved3:4; u8 reserved4:1; u8 af_4k_support:1; u8 reserved5:6; u8 reserved6:3; u8 ra_id_encoding:3; u8 sis_format:2; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved:5; u8 dual_ioa_asymmetric_access:1; u8 dual_ioa_wcache:1; u8 dual_ioa_raid:1; u8 reserved3:4; u8 gscsi_only_ha:1; u8 is_dual_wide:1; u8 is_aux_cache:1; u8 can_attach_to_aux_cache:1; u8 reserved4:6; u8 af_4k_support:1; u8 reserved5:1; u8 sis_format:2; u8 ra_id_encoding:3; u8 reserved6:3; #endif u16 af_block_size; u16 af_ext_cap; u16 vset_ext_cap; u16 gscsi_ext_cap; }; struct ipr_dasd_ucode_header { u8 length[3]; u8 load_id[4]; u8 modification_level[4]; u8 ptf_number[4]; u8 patch_number[4]; }; struct ipr_ioa_ucode_img_desc { #define IPR_IOAF_STR "IOAF" #define IPR_FPGA_STR "FPGA" #define IPR_DRAM_STR "AGIG" char fw_type[4]; #define IPR_IOAF 0 #define IPR_FPGA 1 #define IPR_DRAM 2 u8 reserved[16]; }; struct ipr_ioa_ucode_ext_header { char eyecatcher[8]; /* EXTDLIMG */ u32 image_length; u32 flags; char fw_level[8]; u8 reserved[12]; u32 img_desc_offset; }; struct ipr_ioa_ucode_header { u32 header_length; u32 lid_table_offset; u32 rev_level; #define LINUX_HEADER_RESERVED_BYTES 20 u8 reserved[LINUX_HEADER_RESERVED_BYTES]; char eyecatcher[16]; /* IBMAS400 CCIN */ u32 num_lids; }; struct ipr_fw_images { char file[100]; u32 version; int has_header; char date[20]; }; struct ipr_ioa_vpd { struct ipr_std_inq_data std_inq_data; u8 ascii_part_num[IPR_VPD_PART_NUM_LEN]; u8 reserved[40]; u8 ascii_plant_code[IPR_VPD_PLANT_CODE_LEN]; }; struct ipr_cfc_vpd { u8 peri_dev_type; u8 page_code; u8 reserved1; u8 add_page_len; u8 ascii_len; u8 cache_size[IPR_VPD_CACHE_SIZE_LEN]; struct ipr_std_inq_vpids vpids; u8 revision_level[4]; u8 serial_num[IPR_SERIAL_NUM_LEN]; u8 ascii_part_num[12]; u8 reserved3[40]; u8 ascii_plant_code[IPR_VPD_PLANT_CODE_LEN]; }; struct ipr_dram_vpd { u8 peri_dev_type; u8 page_code; u8 reserved1; u8 add_page_len; u8 ascii_len; u8 dram_size[IPR_VPD_DRAM_SIZE_LEN]; }; struct ipr_cache_cap_vpd { u8 peri_dev_type; u8 page_code; u8 reserved1; u8 add_page_len; u32 cache_cap; u32 data_store_size; u32 write_cache_size; u32 comp_write_cache_size; u32 read_cache_size; u32 comp_read_cache_size; }; struct ipr_dev_identify_vpd { u8 peri_dev_type; u8 page_code; u16 add_page_len; u8 reserved[80]; u8 dev_identify_contxt[40]; }; #define IPR_IOA_MAX_SUPP_LOG_PAGES 16 struct ipr_supp_log_pages { u8 page_code; u8 reserved; u16 length; u8 page[IPR_IOA_MAX_SUPP_LOG_PAGES]; }; struct ipr_sas_phy_log_desc { u8 reserved; u8 phy; u8 reserved2[2]; #if defined (__BIG_ENDIAN_BITFIELD) u8 reseved3:1; u8 attached_dev_type:3; u8 reserved4:4; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved4:4; u8 attached_dev_type:3; u8 reseved3:1; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved5:4; u8 link_rate:4; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 link_rate:4; u8 reserved5:4; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved6:4; u8 attached_ssp_initiator_port:1; u8 attached_stp_initiator_port:1; u8 attached_smp_initiator_port:1; u8 reserved7:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved7:1; u8 attached_smp_initiator_port:1; u8 attached_stp_initiator_port:1; u8 attached_ssp_initiator_port:1; u8 reserved6:4; #endif #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved8:4; u8 attached_ssp_target_port:1; u8 attached_stp_target_port:1; u8 attached_smp_target_port:1; u8 reserved9:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved9:1; u8 attached_smp_target_port:1; u8 attached_stp_target_port:1; u8 attached_ssp_target_port:1; u8 reserved8:4; #endif u8 sas_addr[8]; u8 attached_sas_addr[8]; u8 attached_phy_id; u8 reserved10[7]; u32 invalid_dword_count; u32 running_disparity_error_count; u32 loss_of_dword_sync_count; u32 phy_reset_problem_count; }; struct ipr_sas_port_log_desc { u16 port_num; #if defined (__BIG_ENDIAN_BITFIELD) u8 du:1; u8 ds:1; u8 tsd:1; u8 etc:1; u8 tmc:2; u8 lbin:1; u8 lp:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 lp:1; u8 lbin:1; u8 tmc:2; u8 etc:1; u8 tsd:1; u8 ds:1; u8 du:1; #endif u8 length; u8 proto_id; #define IPR_SAS_PROTOCOL_ID 0x06 u8 reserved[2]; u8 num_phys; #define IPR_IOA_MAX_PHYS 4 struct ipr_sas_phy_log_desc phy[IPR_IOA_MAX_PHYS]; }; #define IPR_SAS_LINK_RATE_NOT_PROGRAMMABLE 0 #define IPR_SAS_LINK_RATE_1_5_Gbps 8 #define IPR_SAS_LINK_RATE_3_0_Gbps 9 #define IPR_SAS_ROUTING_DIRECT 0 #define IPR_SAS_ROUTING_SUB_OR_DIRECT 1 #define IPR_SAS_ROUTING_TABLE_OR_DIRECT 2 struct ipr_sas_phy_info { u32 len; u8 bus; u8 cascade; u8 phy_num; u8 reserved; struct ipr_sas_phy_log_desc phy_log; #if defined (__BIG_ENDIAN_BITFIELD) u8 prog_min_link_rate:4; u8 hw_min_link_rate:4; u8 prog_max_link_rate:4; u8 hw_max_link_rate:4; u8 virtual_phy:1; u8 reserved2:7; u8 reserved3:4; u8 routing_attr:4; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 hw_min_link_rate:4; u8 prog_min_link_rate:4; u8 hw_max_link_rate:4; u8 prog_max_link_rate:4; u8 reserved2:7; u8 virtual_phy:1; u8 routing_attr:4; u8 reserved3:4; #endif }; struct ipr_sas_expander_info { u32 len; u8 bus; u8 cascade; u8 reserved; #if defined (__BIG_ENDIAN_BITFIELD) u8 reserved2:7; u8 sas_1_1_fmt:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 sas_1_1_fmt:1; u8 reserved2:7; #endif struct ipr_std_inq_vpids vpids; u8 prod_rev_level[4]; u8 component_vendor_id[8]; u8 component_id[2]; u8 component_rev_id; u8 reserved3; u8 vendor_spec[8]; u8 enclosure_logical_id[8]; u8 reserved4[3]; u8 num_phys; struct ipr_sas_phy_info phy[1]; }; struct ipr_query_sas_expander_info { u32 len; u8 reserved[3]; u8 num_expanders; struct ipr_sas_expander_info exp[1]; }; struct ipr_global_cache_params_term { #define IPR_CACHE_PARAM_TERM_ID 0x10 u8 term_id; u8 len; #if defined (__BIG_ENDIAN_BITFIELD) u8 enable_caching_dual_ioa_failure:1; u8 disable_caching_requested:1; u8 reserved1:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved1:6; u8 disable_caching_requested:1; u8 enable_caching_dual_ioa_failure:1; #endif u8 reserved2; }; struct ipr_query_ioa_caching_info { u16 len; u8 reserved[2]; #define IPR_MAX_TERMS 512 struct ipr_global_cache_params_term term[IPR_MAX_TERMS]; }; #define for_each_cache_term(term, info) \ for (term = (info)->term; \ ((unsigned long)term) < ((unsigned long)((unsigned long)(info) + ntohs((info)->len)) + 2) && \ ((unsigned long)term + term->len + 2 - (unsigned long)(info)->term) < sizeof((info)->term); \ term = (struct ipr_global_cache_params_term *)(((unsigned long)term) + term->len + 2)) struct ipr_log_page18 { u8 page_code; u8 reserved; u16 length; #define IPR_IOA_MAX_PORTS 32 struct ipr_sas_port_log_desc port[IPR_IOA_MAX_PORTS]; }; #define for_each_port(port, log) \ for (port = (log)->port; \ (unsigned long)port < ((unsigned long)((log)->port) + ntohs(port->length)); \ port = (struct ipr_sas_port_log_desc *)((unsigned long)(&port->length) + port->length + 1)) #define for_each_phy(phy, port) \ for (phy = (port)->phy; \ (phy < ((port)->phy + (port)->num_phys)) && \ (phy < ((port)->phy + IPR_IOA_MAX_PORTS)); phy++) struct ipr_fabric_config_element { u8 type_status; #define IPR_PATH_CFG_TYPE_MASK 0xF0 #define IPR_PATH_CFG_NOT_EXIST 0x00 #define IPR_PATH_CFG_IOA_PORT 0x10 #define IPR_PATH_CFG_EXP_PORT 0x20 #define IPR_PATH_CFG_DEVICE_PORT 0x30 #define IPR_PATH_CFG_DEVICE_LUN 0x40 #define IPR_PATH_CFG_STATUS_MASK 0x0F #define IPR_PATH_CFG_NO_PROB 0x00 #define IPR_PATH_CFG_DEGRADED 0x01 #define IPR_PATH_CFG_FAILED 0x02 #define IPR_PATH_CFG_SUSPECT 0x03 #define IPR_PATH_NOT_DETECTED 0x04 #define IPR_PATH_INCORRECT_CONN 0x05 u8 cascaded_expander; u8 phy; u8 link_rate; #define IPR_PHY_LINK_RATE_MASK 0x0F u32 wwid[2]; }; struct ipr_fabric_descriptor { u16 length; u8 ioa_port; u8 cascaded_expander; u8 phy; u8 path_state; #define IPR_PATH_ACTIVE_MASK 0xC0 #define IPR_PATH_NO_INFO 0x00 #define IPR_PATH_ACTIVE 0x40 #define IPR_PATH_NOT_ACTIVE 0x80 #define IPR_PATH_STATE_MASK 0x0F #define IPR_PATH_STATE_NO_INFO 0x00 #define IPR_PATH_HEALTHY 0x01 #define IPR_PATH_DEGRADED 0x02 #define IPR_PATH_FAILED 0x03 u16 num_entries; struct ipr_fabric_config_element elem[1]; }; struct ipr_fabric_config_element64 { u16 length; #if defined (__BIG_ENDIAN_BITFIELD) u8 cfg_elem_id:2; u8 reserved1:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved1:6; u8 cfg_elem_id:2; #endif u8 reserved2; u8 type_status; u8 reserved3[2]; u8 link_rate; u8 res_path[8]; u32 wwid[4]; }; struct ipr_fabric_descriptor64 { u16 length; #if defined (__BIG_ENDIAN_BITFIELD) u8 fabric_id:2; u8 reserved1:6; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 reserved1:6; u8 fabric_id:2; #endif u8 reserved2[2]; u8 path_state; u8 reserved3; u8 res_path[8]; u32 reserved4; u8 reserved5[2]; u16 num_entries; struct ipr_fabric_config_element64 elem[1]; }; #define for_each_fabric_cfg(fabric, cfg) \ for (cfg = (fabric)->elem; \ cfg < ((fabric)->elem + ntohs((fabric)->num_entries)); \ cfg++) #define for_each_fabric_desc(fabric, info) \ for (fabric = (info)->u.fabric; \ ((unsigned long)fabric) < (((unsigned long)(info)) + ntohl((info)->len) + sizeof((info)->len)); \ fabric = (struct ipr_fabric_descriptor *)(((unsigned long)fabric) + ntohs(fabric->length))) #define for_each_fabric_desc64(fabric, info) \ for (fabric = (info)->u.fabric64; \ ((unsigned long)fabric) < (((unsigned long)(info)) + ntohl((info)->len) + sizeof((info)->len)); \ fabric = (struct ipr_fabric_descriptor64 *)(((unsigned long)fabric) + ntohs(fabric->length))) struct ipr_res_redundancy_info { u32 len; u8 possibly_redundant_paths; u8 healthy_paths; u8 degraded_paths; u8 failed_paths; u8 active_paths; u8 reserved[2]; u8 fabric_descriptors; union { struct ipr_fabric_descriptor fabric[1]; struct ipr_fabric_descriptor64 fabric64[1]; } u; u8 data[16384]; }; struct ses_inquiry_page0 { u8 peri_qual_dev_type; u8 page_code; u8 reserved1; u8 page_length; u8 supported_vpd_page[SES_MAX_NUM_SUPP_INQ_PAGES]; }; struct ses_serial_num_vpd { u8 peri_dev_type; u8 page_code; u8 reserved1; u8 add_page_len; u8 ascii_len; u8 serial_num_head[3]; u8 feature_code[4]; u8 ascii_dash; u8 count[3]; u8 ascii_space; u8 ses_serial_num[7]; }; struct esm_serial_num_vpd { u8 peri_dev_type; u8 page_code; u8 reserved1; u8 page_len; u8 ascii_len; u8 fru_part_number[3]; u8 frb_number[8]; u8 reserved2; u8 serial_num_head[3]; u8 esm_serial_num[12]; u8 reserved3; u8 model_num_head[3]; u8 model_num[4]; u8 reserved4; u8 frb_label_head[3]; u8 frb_label[5]; u8 reserved5; }; struct drive_elem_desc { u8 reserved1[2]; u8 desc_length[2]; u8 reserved2; #define DISK_PHY_LOC_LENGTH 9 u8 disk_physical_loc[DISK_PHY_LOC_LENGTH]; u8 reserved3[2]; u8 form_facter; u8 slot_height; u8 slot_bus; u8 phy_speed; }; struct drive_elem_desc_pg { u8 page_code; u8 reserved1; u16 page_length; u8 reserved2[6]; u16 desc_length; #define DEVICE_ELEM_NUMBER 50 struct drive_elem_desc dev_elem[DEVICE_ELEM_NUMBER]; }; struct ipr_ses_type_desc { u8 elem_type; #define IPR_SES_DEVICE_ELEM 0x01 #define IPR_SES_ESM_ELEM 0x07 #define IPR_SES_ENCLOSURE_ELEM 0x0E u8 num_elems; u8 subenclosure_id; u8 text_len; }; struct ipr_drive_elem_status { #if defined (__BIG_ENDIAN_BITFIELD) u8 select:1; u8 predictive_fault:1; u8 reserved:1; u8 swap:1; u8 status:4; u8 reserved2:1; u8 device_environment:2; u8 slot_id:5; u8 reserved3:4; u8 insert:1; u8 remove:1; u8 identify:1; u8 reserved4:1; u8 reserved5:1; u8 fault_requested:1; u8 fault_sensed:1; u8 reserved6:4; u8 disable_resets:1; #elif defined (__LITTLE_ENDIAN_BITFIELD) u8 status:4; u8 swap:1; u8 reserved:1; u8 predictive_fault:1; u8 select:1; u8 slot_id:5; u8 device_environment:2; u8 reserved2:1; u8 reserved4:1; u8 identify:1; u8 remove:1; u8 insert:1; u8 reserved3:4; u8 disable_resets:1; u8 reserved6:4; u8 fault_sensed:1; u8 fault_requested:1; u8 reserved5:1; #endif }; struct ipr_encl_status_ctl_pg { u8 page_code; u8 health_status; u16 byte_count; u8 reserved1[4]; struct ipr_drive_elem_status elem_status[IPR_NUM_DRIVE_ELEM_STATUS_ENTRIES]; }; struct ipr_ses_config_pg { u8 buf[2048]; }; struct ipr_ses_inquiry_pageC3 { u8 perif_type; u8 page_code; u8 reserved1; u8 page_length; u8 ascii_length; u8 mode_keyword[3]; u8 mode[4]; u8 reserved2; }; static inline int ipr_elem_offset(struct ipr_ses_config_pg *ses_cfg, u8 type) { int i, off; struct ipr_ses_type_desc *desc; u8 *diag = ses_cfg->buf; desc = (struct ipr_ses_type_desc *)&diag[12+diag[11]]; for (i = 0, off = 1; i < diag[10]; off += (desc->num_elems + 1), i++, desc++) if (desc->elem_type == type) return off; return 1; } static inline int ipr_dev_elem_offset(struct ipr_ses_config_pg *ses_cfg) { return ipr_elem_offset(ses_cfg, IPR_SES_DEVICE_ELEM); } static inline int ipr_ses_elem_offset(struct ipr_ses_config_pg *ses_cfg) { return ipr_elem_offset(ses_cfg, IPR_SES_ENCLOSURE_ELEM); } static inline int ipr_esm_elem_offset(struct ipr_ses_config_pg *ses_cfg) { return ipr_elem_offset(ses_cfg, IPR_SES_ESM_ELEM); } static inline struct ipr_drive_elem_status * ipr_get_overall_elem(struct ipr_encl_status_ctl_pg *ses_data, struct ipr_ses_config_pg *ses_cfg) { return &(ses_data->elem_status[ipr_dev_elem_offset(ses_cfg) - 1]); } static inline struct ipr_ses_type_desc *ipr_get_elem(struct ipr_ses_config_pg *ses_cfg, u8 type) { int i; struct ipr_ses_type_desc *desc; u8 *diag = ses_cfg->buf; desc = (struct ipr_ses_type_desc *)&diag[12+diag[11]]; for (i = 0; i < diag[10]; i++, desc++) if (desc->elem_type == type) return desc; return NULL; } static inline struct ipr_ses_type_desc *ipr_get_dev_elem(struct ipr_ses_config_pg *ses_cfg) { return ipr_get_elem(ses_cfg, IPR_SES_DEVICE_ELEM); } static inline struct ipr_drive_elem_status * ipr_get_enclosure_elem(struct ipr_encl_status_ctl_pg *ses_data, struct ipr_ses_config_pg *ses_cfg) { return &(ses_data->elem_status[ipr_ses_elem_offset(ses_cfg) - 1]); } static inline struct ipr_drive_elem_status * ipr_get_esm_elem(struct ipr_encl_status_ctl_pg *ses_data, struct ipr_ses_config_pg *ses_cfg) { return &(ses_data->elem_status[ipr_esm_elem_offset(ses_cfg) - 1]); } static inline int ipr_max_dev_elems(struct ipr_ses_config_pg *ses_cfg) { struct ipr_ses_type_desc *desc = ipr_get_dev_elem(ses_cfg); if (desc && desc->num_elems < IPR_NUM_DRIVE_ELEM_STATUS_ENTRIES) return desc->num_elems; return IPR_NUM_DRIVE_ELEM_STATUS_ENTRIES; } #define for_each_elem_status(elem, sd, sc) \ for (elem = &((sd)->elem_status[ipr_dev_elem_offset(sc)]); \ elem < &((sd)->elem_status[(ntohs((sd)->byte_count)-4)/sizeof((sd)->elem_status[0])]) && \ elem < &((sd)->elem_status[ipr_dev_elem_offset(sc) + ipr_max_dev_elems(sc)]) && \ elem < &((sd)->elem_status[IPR_NUM_DRIVE_ELEM_STATUS_ENTRIES]); elem++) int sg_ioctl(int, u8 *, void *, u32, u32, struct sense_data_t *, u32); int sg_ioctl_noretry(int, u8 *, void *, u32, u32, struct sense_data_t *, u32); int ipr_change_multi_adapter_assignment(struct ipr_ioa *, int, int); int set_ha_mode(struct ipr_ioa *, int); int set_preferred_primary(struct ipr_ioa *, int); void check_current_config(bool); int num_device_opens(int, int, int, int); int open_and_lock(char *); void tool_init(int); void exit_on_error(char *, ...); bool is_af_blocked(struct ipr_dev *, int); int ipr_query_array_config(struct ipr_ioa *, bool, bool, bool, int, struct ipr_array_query_data *); int __ipr_query_array_config(struct ipr_ioa *, int, bool, bool, bool, int, struct ipr_array_query_data *); int ipr_query_command_status(struct ipr_dev *, void *); int ipr_query_io_dev_port(struct ipr_dev *, struct ipr_query_io_port *); int ipr_mode_sense(struct ipr_dev *, u8, void *); int ipr_mode_select(struct ipr_dev *, void *, int); int ipr_log_sense(struct ipr_dev *, u8, void *, u16); int ipr_is_log_page_supported(struct ipr_dev *, u8); int ipr_reset_device(struct ipr_dev *); int ipr_re_read_partition(struct ipr_dev *); int ipr_read_capacity(struct ipr_dev *, void *); int ipr_read_capacity_16(struct ipr_dev *, void *); int ipr_query_resource_state(struct ipr_dev *, void *); void ipr_allow_restart(struct ipr_dev *, int); int ipr_get_logical_block_size(struct ipr_dev *); bool ipr_is_af_blk_size(struct ipr_ioa *, int); void ipr_set_manage_start_stop(struct ipr_dev *); int ipr_start_stop_start(struct ipr_dev *); int ipr_start_stop_stop(struct ipr_dev *); int ipr_stop_array_protection(struct ipr_ioa *); int ipr_remove_hot_spare(struct ipr_ioa *); int ipr_start_array_protection(struct ipr_ioa *, int, int); int ipr_migrate_array_protection(struct ipr_ioa *, struct ipr_array_query_data *, int, int, int); int ipr_set_array_asym_access(struct ipr_ioa *); int ipr_add_hot_spare(struct ipr_ioa *); int ipr_rebuild_device_data(struct ipr_ioa *); int ipr_resync_array(struct ipr_ioa *); int ipr_test_unit_ready(struct ipr_dev *, struct sense_data_t *); int ipr_format_unit(struct ipr_dev *); void ipr_format_res_path(u8 *, char *, int); void ipr_format_res_path_wo_hyphen(u8 *, char *, int); int ipr_add_array_device(struct ipr_ioa *, int, struct ipr_array_query_data *); int ipr_reclaim_cache_store(struct ipr_ioa *, int, void *); int ipr_evaluate_device(struct ipr_dev *, u32); int ipr_query_res_redundancy_info(struct ipr_dev *, struct ipr_res_redundancy_info *); int ipr_query_sas_expander_info(struct ipr_ioa *, struct ipr_query_sas_expander_info *); int ipr_query_cache_parameters(struct ipr_ioa *, void *, int); int ipr_disrupt_device(struct ipr_dev *); int ipr_inquiry(struct ipr_dev *, u8, void *, u8); int ipr_query_res_addr_aliases(struct ipr_ioa *, struct ipr_res_addr *, struct ipr_res_addr_aliases *); int ipr_query_res_path_aliases(struct ipr_ioa *, struct ipr_res_path *, struct ipr_res_path_aliases *); void ipr_convert_res_path_to_bytes(struct ipr_dev *); void ipr_format_res_path(u8 *, char *, int); void ipr_reset_adapter(struct ipr_ioa *); void ipr_scan(struct ipr_ioa *, int, int, int); int ipr_read_host_attr(struct ipr_ioa *, char *, void *, size_t); int ipr_write_host_attr(struct ipr_ioa *, char *, void *, size_t); int ipr_read_dev_attr(struct ipr_dev *, char *, char *, size_t); int ipr_write_dev_attr(struct ipr_dev *, char *, char *); int ipr_suspend_device_bus(struct ipr_dev *, struct ipr_res_addr *, u8); int ipr_resume_device_bus(struct ipr_dev *, struct ipr_res_addr *); int ipr_can_remove_device(struct ipr_dev *); int ipr_receive_diagnostics(struct ipr_dev *, u8, void *, int); int ipr_send_diagnostics(struct ipr_dev *, void *, int); int ipr_get_bus_attr(struct ipr_ioa *, struct ipr_scsi_buses *); void ipr_modify_bus_attr(struct ipr_ioa *, struct ipr_scsi_buses *); int ipr_set_bus_attr(struct ipr_ioa *, struct ipr_scsi_buses *, int); int ipr_write_buffer(struct ipr_dev *, void *, int); int enable_af(struct ipr_dev *); int get_max_bus_speed(struct ipr_ioa *, int); int ipr_get_ioa_attr(struct ipr_ioa *, struct ipr_ioa_attr *); int ipr_get_dev_attr(struct ipr_dev *, struct ipr_disk_attr *); int ipr_modify_dev_attr(struct ipr_dev *, struct ipr_disk_attr *); int ipr_set_ioa_attr(struct ipr_ioa *, struct ipr_ioa_attr *, int); int ipr_modify_ioa_attr(struct ipr_ioa *, struct ipr_ioa_attr *); int ipr_set_dev_attr(struct ipr_dev *, struct ipr_disk_attr *, int); int set_active_active_mode(struct ipr_ioa *, int); int ipr_set_format_completed_bit(struct ipr_dev *); int ipr_set_ses_mode(struct ipr_dev *, int); int get_ioa_caching(struct ipr_ioa *); int ipr_change_cache_parameters(struct ipr_ioa *, int); int ipr_query_dasd_timeouts(struct ipr_dev *, struct ipr_query_dasd_timeouts *); int get_ioa_firmware_image_list(struct ipr_ioa *, struct ipr_fw_images **); int get_dasd_firmware_image_list(struct ipr_dev *, struct ipr_fw_images **); int get_ses_firmware_image_list(struct ipr_dev *, struct ipr_fw_images **); int ipr_update_ioa_fw(struct ipr_ioa *, struct ipr_fw_images *, int); int ipr_update_disk_fw(struct ipr_dev *, struct ipr_fw_images *, int); int ipr_init_dev(struct ipr_dev *); int ipr_init_new_dev(struct ipr_dev *); int ipr_init_ioa(struct ipr_ioa *); int device_supported(struct ipr_dev *); struct ipr_dev *get_dev_from_addr(struct ipr_res_addr *res_addr); struct ipr_dev *get_dev_from_handle(struct ipr_ioa *ioa, u32 res_handle); void ipr_daemonize(); struct unsupported_af_dasd *get_unsupp_af(struct ipr_std_inq_data *, struct ipr_dasd_inquiry_page3 *); bool disk_needs_msl(struct unsupported_af_dasd *, struct ipr_std_inq_data *); void ipr_add_zeroed_dev(struct ipr_dev *); void ipr_update_qac_with_zeroed_devs(struct ipr_ioa *); void ipr_cleanup_zeroed_devs(); void ipr_del_zeroed_dev(struct ipr_dev *); int ipr_device_is_zeroed(struct ipr_dev *); struct ipr_array_cap_entry *get_raid_cap_entry(struct ipr_supported_arrays *, u8 ); char *get_prot_level_str(struct ipr_supported_arrays *, int); u32 get_dev_fw_version(struct ipr_dev *); u32 get_ioa_fw_version(struct ipr_ioa *); int ipr_disable_qerr(struct ipr_dev *); void ipr_log_ucode_error(struct ipr_ioa *); u32 get_dasd_ucode_version(char *, int); u32 get_ses_ucode_version(char *ucode_file); const char *get_bus_desc(struct ipr_ioa *); const char *get_ioa_desc(struct ipr_ioa *); int ioa_is_spi(struct ipr_ioa *); int __ioa_is_spi(struct ipr_ioa *); int page0x0a_setup(struct ipr_dev *); int handle_events(void (*) (void), int, void (*) (char *)); struct ipr_ioa *find_ioa(int); int parse_option(char *); struct ipr_dev *find_blk_dev(char *); struct ipr_dev *find_gen_dev(char *); struct ipr_dev *find_dev(char *); int ipr_cmds_per_lun(struct ipr_ioa *); void scsi_host_kevent(char *, int (*)(struct ipr_ioa *)); void scsi_dev_kevent(char *, struct ipr_dev *(*)(char *), int (*)(struct ipr_dev *)); int format_req(struct ipr_dev *); struct ipr_dev *get_vset_from_array(struct ipr_ioa *, struct ipr_dev *); struct ipr_dev *get_array_from_vset(struct ipr_ioa *, struct ipr_dev *); struct sysfs_dev * ipr_find_sysfs_dev(struct ipr_dev *, struct sysfs_dev *); void ipr_add_sysfs_dev(struct ipr_dev *, struct sysfs_dev **, struct sysfs_dev **); void ipr_del_sysfs_dev(struct ipr_dev *, struct sysfs_dev **, struct sysfs_dev **); struct ipr_dev *ipr_sysfs_dev_to_dev(struct sysfs_dev *); struct ipr_array_cap_entry *get_cap_entry(struct ipr_supported_arrays *, char *); int ipr_get_blk_size(struct ipr_dev *); u32 get_ioa_ucode_version(char *); int ipr_improper_device_type(struct ipr_dev *); int ipr_get_fw_version(struct ipr_dev *, u8 release_level[4]); int ipr_get_live_dump(struct ipr_ioa *); static inline u32 ipr_get_dev_res_handle(struct ipr_ioa *ioa, struct ipr_dev_record *dev_rcd) { if (ioa->sis64) return dev_rcd->type3.resource_handle; else return dev_rcd->type2.resource_handle; } static inline u32 ipr_get_arr_res_handle(struct ipr_ioa *ioa, struct ipr_array_record *array_rcd) { if (ioa->sis64) return array_rcd->type3.resource_handle; else return array_rcd->type2.resource_handle; } static inline int ipr_is_device_record(int record_id) { if ((record_id == IPR_RECORD_ID_DEVICE_RECORD) || (record_id == IPR_RECORD_ID_DEVICE_RECORD_3)) return 1; else return 0; } static inline int ipr_is_vset_record(int record_id) { if ((record_id == IPR_RECORD_ID_ARRAY_RECORD) || (record_id == IPR_RECORD_ID_VSET_RECORD_3)) return 1; else return 0; } static inline int ipr_is_array_record(int record_id) { if ((record_id == IPR_RECORD_ID_ARRAY_RECORD) || (record_id == IPR_RECORD_ID_ARRAY_RECORD_3)) return 1; else return 0; } static inline int ipr_is_af_dasd_device(struct ipr_dev *device) { if ((device->qac_entry != NULL) && (ipr_is_device_record(device->qac_entry->record_id))) return 1; else return 0; } static inline int ipr_is_remote_af_dasd_device(struct ipr_dev *device) { if ((device->qac_entry != NULL) && (ipr_is_device_record(device->qac_entry->record_id) && device->dev_rcd->current_asym_access_state == IPR_ACTIVE_NON_OPTIMIZED)) return 1; else return 0; } static inline int ipr_is_volume_set(struct ipr_dev *device) { if ((device->qac_entry != NULL) && (ipr_is_vset_record(device->qac_entry->record_id))) return 1; else return 0; } static inline int ipr_is_array(struct ipr_dev *device) { if ((device->qac_entry != NULL) && (ipr_is_array_record(device->qac_entry->record_id))) return 1; else return 0; } static inline int ipr_is_hidden(struct ipr_dev *device) { if (ipr_is_af_dasd_device(device) || ((device->scsi_dev_data) && (device->scsi_dev_data->type == 3))) //FIXME SES type. return 1; else return 0;} static inline int ipr_is_hot_spare(struct ipr_dev *device) { struct ipr_dev_record *dev_record = (struct ipr_dev_record *)device->qac_entry; if ((dev_record != NULL) && (ipr_is_device_record(dev_record->common.record_id)) && (dev_record->is_hot_spare)) return 1; else return 0; } static inline int ipr_is_array_member(struct ipr_dev *device) { struct ipr_dev_record *dev_record = (struct ipr_dev_record *)device->qac_entry; if ((dev_record != NULL) && (dev_record->array_member)) return 1; else return 0; } static inline int ipr_is_af(struct ipr_dev *device) { if (device->qac_entry != NULL) return 1; else return 0; } static inline int ipr_is_gscsi(struct ipr_dev *dev) { if (!dev->qac_entry && dev->scsi_dev_data && dev->scsi_dev_data->type == TYPE_DISK) return 1; else return 0; } static inline int ipr_is_ses(struct ipr_dev *dev) { if (dev->scsi_dev_data && dev->scsi_dev_data->type == TYPE_ENCLOSURE) return 1; else return 0; } static inline void ipr_strncpy_0(char *dest, char *source, int length) { memcpy(dest, source, length); dest[length] = '\0'; } static inline void ipr_strncpy_0n(char *dest, char *source, int length) { char *ch; memcpy(dest, source, length); dest[length] = '\0'; ch = strchr(dest, '\n'); if (ch) *ch = '\0'; } #define dprintf(...) \ do { \ if (ipr_debug) \ fprintf(stderr, __VA_ARGS__);\ } while(0) #define ENTER dprintf("Entering %s\n", __FUNCTION__); #define LEAVE dprintf("Leaving %s\n", __FUNCTION__); #define syslog_dbg(...) \ do { \ if (ipr_debug) \ syslog(LOG_ERR, __VA_ARGS__);\ } while(0) #define ra_dbg(ra, fmt, ...) \ syslog_dbg("%d:%d:%d:%d: " fmt, (ra)->host, (ra)->bus, \ (ra)->target, (ra)->lun, ##__VA_ARGS__); \ #define scsi_log(level, dev, fmt, ...) \ do { \ if (dev->scsi_dev_data && !dev->ioa->ioa_dead) { \ syslog(level, "%d:%d:%d:%d: " fmt, dev->ioa->host_num, \ dev->scsi_dev_data->channel, dev->scsi_dev_data->id, \ dev->scsi_dev_data->lun, ##__VA_ARGS__); \ } \ } while (0) #define scsi_dbg(dev, fmt, ...) \ do { \ if ((dev)->scsi_dev_data) { \ syslog_dbg("%d:%d:%d:%d: " fmt, (dev)->ioa->host_num, \ (dev)->scsi_dev_data->channel, (dev)->scsi_dev_data->id, \ (dev)->scsi_dev_data->lun, ##__VA_ARGS__); \ } \ } while (0) #define scsi_info(dev, fmt, ...) \ scsi_log(LOG_NOTICE, dev, fmt, ##__VA_ARGS__) #define scsi_err(dev, fmt, ...) \ scsi_log(LOG_ERR, dev, fmt, ##__VA_ARGS__) #define scsi_warn(dev, fmt, ...) \ scsi_log(LOG_WARNING, dev, fmt, ##__VA_ARGS__) #define scsi_cmd_err(dev, sense, cmd, rc) \ do { \ if ((((sense)->error_code & 0x7F) != 0x70) || \ (((sense)->sense_key & 0x0F) != 0x05) || ipr_debug) { \ scsi_err(dev, "%s failed. rc=%d, SK: %X ASC: %X ASCQ: %X\n",\ cmd, rc, (sense)->sense_key & 0x0f, (sense)->add_sense_code, \ (sense)->add_sense_code_qual); \ } \ } while (0) #define scsi_cmd_dbg(dev, sense, cmd, rc) \ do { \ if (rc && ipr_debug) { \ scsi_err(dev, "%s failed. rc=%d, SK: %X ASC: %X ASCQ: %X\n",\ cmd, rc, (sense)->sense_key & 0x0f, (sense)->add_sense_code, \ (sense)->add_sense_code_qual); \ } \ } while (0) #define ioa_log(level, ioa, fmt, ...) \ syslog(level, "%s: " fmt, ioa->pci_address, ##__VA_ARGS__) #define ioa_info(ioa, fmt, ...) \ ioa_log(LOG_NOTICE, ioa, fmt, ##__VA_ARGS__) #define ioa_err(ioa, fmt, ...) \ ioa_log(LOG_ERR, ioa, fmt, ##__VA_ARGS__) #define ioa_dbg(ioa, fmt, ...) \ syslog_dbg("%s: " fmt, ioa->pci_address, ##__VA_ARGS__) #define ioa_cmd_err(ioa, sense, cmd, rc) \ do { \ if (!ioa->ioa_dead) { \ if ((((sense)->error_code & 0x7F) != 0x70) || \ (((sense)->sense_key & 0x0F) != 0x05) || ipr_debug) { \ ioa_err(ioa, "%s failed. rc=%d, SK: %X ASC: %X ASCQ: %X\n",\ cmd, rc, (sense)->sense_key & 0x0f, (sense)->add_sense_code, \ (sense)->add_sense_code_qual); \ } \ } \ } while (0) #endif /* iprlib_h */ iprutils/iprconfig.h0000644000000000000000000016367012275251125013577 0ustar rootroot#ifndef iprconfig_h #define iprconfig_h /** * IBM IPR adapter configuration utility * * (C) Copyright 2004 * International Business Machines Corporation and others. * All Rights Reserved. This program and the accompanying * materials are made available under the terms of the * Common Public License v1.0 which accompanies this distribution. * **/ #include #define _(string) gettext(string) #define __(string) (string) #define EXIT_FLAG 0x8000 /* stops at given screen on exit call */ #define CANCEL_FLAG 0x4000 /* stops at given screen on quit call */ #define REFRESH_FLAG 0x2000 /* refreshes screen on quit or exit */ #define TOGGLE_FLAG 0x1000 #define FWD_FLAG 0x0800 #define CONFIRM_FLAG 0x0400 #define CONFIRM_REC_FLAG 0x0200 #define MENU_FLAG 0x0100 #define ENTER_FLAG 0x0080 #define NUM_OPTS(x) (sizeof(x)/sizeof(struct screen_opts)) #define REFRESH_SCREEN -13 #define TOGGLE_SCREEN -14 #define INVALID_OPTION_STATUS 2 #define PGUP_STATUS 3 #define TOP_STATUS 4 #define PGDN_STATUS 5 #define BTM_STATUS 6 #define MAX_FIELD_SIZE 39 typedef struct info_container i_container; struct info_container { i_container *next_item; /* reference to next info_container */ char field_data[MAX_FIELD_SIZE + 1]; /* stores characters entered into a user-entry field */ void *data; /* stores a field pointer */ int y; /* cursor y position of user selection */ int x; /* cursor x position of user selection */ }; #define for_each_icon(icon) for (icon = i_con_head; icon; icon = icon->next_item) struct screen_output { int rc; i_container *i_con; }; typedef struct function_output fn_out; struct function_output { fn_out *next; /* reference to next fn_out if >1 */ int index; /* used to plop text into # spots on text.msg */ int cat_offset; /* varies text set used in text.msg -> esp dif platforms */ char *text; /* holds "special" data to be passed back to the screen */ }; int main_menu(i_container * i_con); int disk_status(i_container * i_con); int device_details(i_container * i_con); int path_details(i_container * i_con); int raid_screen(i_container * i_con); int raid_status(i_container * i_con); int raid_stop(i_container * i_con); int confirm_raid_stop(i_container * i_con); int do_confirm_raid_stop(i_container * i_con); int raid_start(i_container * i_con); int raid_start_loop(i_container * i_con); int configure_raid_start(i_container * i_con); int configure_raid_parameters(i_container * i_con); int confirm_raid_start(i_container * i_con); int raid_start_complete(); int raid_include(i_container * i_con); int configure_raid_include(i_container * i_con); int confirm_raid_include(i_container * i_con); int confirm_raid_include_device(i_container * i_con); int disk_unit_recovery(i_container * i_con); int concurrent_add_device(i_container *i_con); int concurrent_remove_device(i_container *i_con); int verify_conc_maint(i_container * i_con); int path_status(i_container * i_con); int init_device(i_container * i_con); int confirm_init_device(i_container * i_con); int send_dev_inits(i_container * i_con); int reclaim_cache(i_container * i_con); int confirm_reclaim(i_container * i_con); int reclaim_warning(i_container * i_con); int reclaim_result(i_container * i_con); int af_include(i_container * i_con); int af_remove(i_container * i_con); int hot_spare_screen(i_container *i_con); int add_hot_spare(i_container * i_con); int remove_hot_spare(i_container * i_con); int hot_spare(i_container * i_con, int action); int select_hot_spare(i_container * i_con, int action); int confirm_hot_spare(int action); int hot_spare_complete(int action); int raid_migrate(i_container * i_con); int asym_access(i_container *i_con); int asym_access_menu(i_container * i_con); int raid_rebuild(i_container * i_con); int confirm_raid_rebuild(i_container * i_con); int raid_resync(i_container * i_con); int confirm_raid_resync(i_container * i_con); int battery_maint(i_container * i_con); int enclosures_maint(i_container * i_con); int battery_fork(i_container * i_con); int enclosures_fork(i_container * i_con); int force_battery_error(i_container *i_con); int enable_battery(i_container *i_con); int ipr_suspend_disk_enclosure(i_container *i_con); int ipr_resume_disk_enclosure(i_container *i_con); int bus_config(i_container * i_con); int change_bus_attr(i_container * i_con); int confirm_change_bus_attr(i_container *i_con); int driver_config(i_container * i_con); int change_driver_config(i_container *i_con); int disk_config(i_container * i_con); int ioa_config(i_container * i_con); int change_disk_config(i_container *); int change_ioa_config(i_container *); int download_ucode(i_container *); int choose_ucode(i_container *); int log_menu(i_container *); int ibm_storage_log_tail(i_container *); int ibm_storage_log(i_container *); int kernel_log(i_container *); int iprconfig_log(i_container *); int kernel_root(i_container *); int confirm_kernel_root(i_container *); int confirm_kernel_root_change(i_container *); int confirm_kernel_root_change2(i_container *); int set_default_editor(i_container *); int confirm_set_default_editor(i_container *); int confirm_set_default_editor_change(i_container *); int confirm_set_default_editor_change2(i_container *); int restore_log_defaults(i_container *); int ibm_boot_log(i_container *); int exit_confirmed(i_container *); static int raid_create_check_num_devs(struct ipr_array_cap_entry *, int, int); /* constant strings */ const char *no_dev_found = __("No devices found"); const char *wait_for_next_screen = __("Please wait for the next screen."); /* others */ int menu_test(i_container * i_con); ITEM **menu_test_menu(int field_index); typedef struct screen_node s_node; struct screen_opts { int (*screen_function) (i_container *); char *key; char *list_str; }; struct screen_node { int rc_flags; int f_flags; int num_opts; struct screen_opts *options; char *title; char *body; char *header[10]; }; struct screen_opts null_opt[] = { {NULL, "\n"} }; struct screen_opts main_menu_opt[] = { {disk_status, "1", __("Display hardware status")}, {raid_screen, "2", __("Work with disk arrays")}, {disk_unit_recovery, "3", __("Work with disk unit recovery")}, {bus_config, "4", __("Work with SCSI bus configuration")}, {driver_config, "5", __("Work with driver configuration")}, {disk_config, "6", __("Work with disk configuration")}, {ioa_config, "7", __("Work with adapter configuration")}, {download_ucode, "8", __("Download microcode")}, {log_menu, "9", __("Analyze log")}, }; s_node n_main_menu = { .rc_flags = (EXIT_FLAG | CANCEL_FLAG), .f_flags = (EXIT_FLAG), .num_opts = NUM_OPTS(main_menu_opt), .options = &main_menu_opt[0], .title = __("IBM Power RAID Configuration Utility") }; struct screen_opts exit_confirm_opt[] = { {main_menu, "1", __("Return to main menu")}, {exit_confirmed, "2", __("Exit iprconfig")}, }; s_node n_exit_confirm = { .num_opts = NUM_OPTS(exit_confirm_opt), .options = &exit_confirm_opt[0], .title = __("Confirm Exit"), .header = { __("One or more disks is currently known to be zeroed. Exiting now " "will cause this state to be lost, resulting in longer array creation times\n\n"), "" } }; struct screen_opts disk_status_opt[] = { {device_details, "\n"} }; s_node n_disk_status = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(disk_status_opt), .options = &disk_status_opt[0], .title = __("Display Hardware Status"), .header = { __("Type option, press Enter.\n"), __(" 1=Display hardware resource information details\n\n"), "" } }; s_node n_adapter_details = { .rc_flags = (CANCEL_FLAG), .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG | FWD_FLAG), .title = __("IOA Hardware Resource Information Details") }; s_node n_device_details = { .rc_flags = (CANCEL_FLAG), .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG | FWD_FLAG), .title = __("Disk Unit Hardware Resource Information Details") }; s_node n_vset_details = { .rc_flags = (CANCEL_FLAG), .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG | FWD_FLAG), .title = __("Disk Array Information Details") }; s_node n_ses_details = { .rc_flags = (CANCEL_FLAG), .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG | FWD_FLAG), .title = __("Disk Enclosure Information Details") }; struct screen_opts raid_screen_opt[] = { {raid_status, "1", __("Display disk array status")}, {raid_start, "2", __("Create a disk array")}, {raid_stop, "3", __("Delete a disk array")}, {raid_include, "4", __("Add a device to a disk array")}, {af_include, "5", __("Format device for RAID function")}, {af_remove, "6", __("Format device for JBOD function")}, {hot_spare_screen, "7", __("Work with hot spares")}, {asym_access, "8", __("Work with asymmetric access")}, {raid_resync, "9", __("Force RAID Consistency Check")}, {raid_migrate, "0", __("Migrate disk array protection")}, }; struct screen_opts hot_spare_opt[] = { {add_hot_spare, "1", __("Create a hot spare")}, {remove_hot_spare, "2", __("Delete a hot spare")}, }; s_node n_asym_access = { .rc_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Array Asymmetric Access"), .header = { __("Select the disk array path.\n\n"), __("Type choice, press Enter.\n"), __(" 1=change asymmetric access for a disk array\n\n"), "" } }; s_node n_asym_access_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Setting Array Asymmetric Access Failed"), .header = { __("There are no arrays eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o Active/Active mode is not enabled on the IOAs.\n"), __("o An IOA needs updated microcode in order to support " "active/active configurations.\n"), __("o None of the disk arrays in the system are capable of " "changing asymmetric access attributes.\n"), __("o There are no disk arrays in the system.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference " "code to correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have " "reported to the system. Retry the operation.\n"), "" } }; struct screen_opts change_array_asym_access_opt[] = { {asym_access_menu, "c"}, {NULL, "\n"} }; s_node n_change_array_asym_access = { .rc_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG), .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG | FWD_FLAG | MENU_FLAG), .num_opts = NUM_OPTS(change_array_asym_access_opt), .options = &change_array_asym_access_opt[0], .title = __("Change Asymmetric Access Configuration of Array"), .header = { __("Current array asymmetric access configuration is shown. To " "change setting hit 'c' for options menu. Highlight " "desired option then hit Enter.\n"), __(" c=Change Setting\n\n"), "" } }; s_node n_raid_migrate_complete = { .title = __("Migrate Disk Array Status"), .body = __("You selected to migrate a disk array") }; s_node n_confirm_raid_migrate = { .rc_flags = (CANCEL_FLAG), .f_flags = (CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Confirm Migrate a Disk Array"), .header = { __("ATTENTION: Disk array will be migrated.\n\n"), __("Press Enter to continue.\n"), __(" q=Cancel to return and change your choice.\n\n"), "" } }; s_node n_raid_migrate = { .rc_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG ), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Migrate Disk Array Protection"), .header = { __("Select only one disk array for migration.\n\n"), __("Type choice, press Enter.\n"), __(" 1=migrate protection for a disk array\n\n"), "" } }; s_node n_raid_migrate_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Disk Array Migration Failed"), .header = { __("There are no arrays eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are no disk arrays in the system.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference " "code to correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have " "reported to the system. Retry the operation.\n"), __("o There are not enough unused AF disks for the migration.\n"), "" } }; s_node n_raid_migrate_add_disks = { .rc_flags = (CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Select Disk Units for Migration"), .header = { __("Type option, press Enter.\n"), __(" 1=Select\n\n"), "" } }; s_node n_hot_spare_screen = { .rc_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG), .num_opts = NUM_OPTS(hot_spare_opt), .options = &hot_spare_opt[0], .title = __("Work with Hot Spares") }; s_node n_raid_screen = { .rc_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG), .num_opts = NUM_OPTS(raid_screen_opt), .options = &raid_screen_opt[0], .title = __("Work with Disk Arrays") }; s_node n_raid_status = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(disk_status_opt), .options = &disk_status_opt[0], .title = __("Display Disk Array Status"), .header = { __("Type option, press Enter.\n"), __(" 1=Display hardware resource information details\n\n"), "" } }; struct screen_opts raid_stop_opt[] = { {confirm_raid_stop, "\n"} }; s_node n_raid_stop = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG ), .num_opts = NUM_OPTS(raid_stop_opt), .options = &raid_stop_opt[0], .title = __("Delete a Disk Array"), .header = { __("Select the disk array(s) to delete.\n\n"), __("Type choice, press Enter.\n"), __(" 1=delete a disk array\n\n"), "" } }; s_node n_raid_stop_fail = { .title = __("Delete a Disk Array Failed"), .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .header = { __("There are no disks eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are no disk arrays in the system.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference code to " "correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have " "reported to the system. Retry the operation.\n"), __("o The disks are missing.\n"), "" } }; struct screen_opts confirm_raid_stop_opt[] = { {do_confirm_raid_stop, "\n"} }; s_node n_confirm_raid_stop = { .rc_flags = (CANCEL_FLAG), .f_flags = (CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(confirm_raid_stop_opt), .options = &confirm_raid_stop_opt[0], .title = __("Confirm Delete a Disk Array"), .header = { __("ATTENTION: Disk array will be deleted.\n\n"), __("Press Enter to continue.\n"), __(" q=Cancel to return and change your choice.\n\n"), "" } }; struct screen_opts raid_start_opt[] = { {raid_start_loop, "\n"} }; s_node n_raid_start = { .rc_flags = (CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(raid_start_opt), .options = &raid_start_opt[0], .title = __("Create a Disk Array"), .header = { __("Select the adapter.\n\n"), __("Type choice, press Enter.\n"), __(" 1=create a disk array\n\n"), "" } }; s_node n_raid_start_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Create a Disk Array Failed"), .header = { __("There are no disks eligible for the selected operation due " "to one or more of the following reasons:\n\n"), __("o There are not enough advanced function disks in the system.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference code to " "correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have " "reported to the system. Retry the operation.\n"), __("o The disks are missing.\n"), "" } }; s_node n_configure_raid_start = { .rc_flags = (CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Select Disk Units for Disk Array"), .header = { __("Type option, press Enter.\n"), __(" 1=Select\n\n"), "" } }; s_node n_confirm_raid_start = { .f_flags = (CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Confirm Create Disk Array"), .header = { __("Press Enter to continue.\n"), __(" q=Cancel to return and change your choice.\n\n"), "" } }; s_node n_raid_start_complete = { .title = __("Create Disk Array Status"), .body = __("You selected to create a disk array") }; struct screen_opts raid_include_opt[] = { {configure_raid_include, "\n"} }; s_node n_raid_include = { .rc_flags = (CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(raid_include_opt), .options = &raid_include_opt[0], .title = __("Add Devices to a Disk Array"), .header = { __("Select the disk array.\n\n"), __("Type choice, press Enter.\n"), __(" 1=Select disk array\n\n"), "" } }; s_node n_raid_include_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Add Devices to a Disk Array Failed"), .header = { __("There are no disks eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are no disk arrays in the system.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference " "code to correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have" "reported to the system. Retry the operation.\n"), __("o The disks are missing.\n"), "" } }; s_node n_configure_raid_include = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Add Devices to a Disk Array"), .header = { __("Select the devices to be included in the disk array\n\n"), __("Type choice, press Enter.\n"), __(" 1=Add a Device to a Disk Array\n\n"), "" } }; s_node n_configure_raid_include_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Add Devices to a Disk Array Failed"), .header = { __("There are no disks eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are not enough disks available to be included.\n"), __("o Not all disks attached to an IOA have reported to the " "system. Retry the operation.\n"), __("o The disk to be included must be the same or greater " "capacity than the smallest device in the disk array and " "be formatted correctly\n"), __("o The disk is not supported for the requested operation\n"), "" } }; s_node n_confirm_raid_include = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Confirm Devices to Add to a Disk Array"), .header = { __("All listed disks will be added to the disk array.\n"), __("ATTENTION: Disks that have not been zeroed with be formatted first.\n"), __("Press Enter to confirm your choice to have the system " "include the selected disks in the disk array\n"), __(" q=Cancel to return and change your choice.\n\n"), "" } }; s_node n_dev_include_complete = { .title = __("Add Devices to a Disk Array Status"), .header = { __("The operation to add disks to the disk array " "will be done in two phases. The disks will first be " "formatted, then added to the disk array."), __("You selected to add disks to a disk array"), "" } }; s_node n_af_include_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Format Device for RAID Function Failed"), .header = { __("There are no disks eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are no eligible disks in the system.\n"), __("o All disks are already formatted for RAID Function.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference code to " "correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have " "reported to the system. Retry the operation.\n"), __("o The disks are missing.\n"), "" } }; s_node n_af_remove_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Format Device for JBOD Function (512) Failed"), .header = { __("There are no disks eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are no eligible disks in the system.\n"), __("o All disks are already formatted for JBOD function.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference code to " "correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have " "reported to the system. Retry the operation.\n"), __("o The disks are missing.\n"), "" } }; s_node n_add_hot_spare_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Create Hot Spare Failed"), .header = { __("There are no disks eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are no eligible disks in the system.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference code to " "correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have " "reported to the system. Retry the operation.\n"), __("o The disks are missing.\n"), "" } }; s_node n_remove_hot_spare_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Delete Hot Spare Failed"), .header = { __("There are no disks eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are no eligible disks in the system.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference code to " "correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have " "reported to the system. Retry the operation.\n"), __("o The disks are missing.\n"), "" } }; s_node n_add_hot_spare = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Create a Hot Spare Device"), .header = { __("Select the adapter on which disks will be " "configured as hot spares\n\n"), __("Type choice, press Enter.\n"), __(" 1=Select adapter\n\n"), "" }, }; s_node n_remove_hot_spare = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Delete a Hot Spare Device"), .header = { __("Select the adapter on which hot spares will be deleted\n\n"), __("Type choice, press Enter.\n"), __(" 1=Select adapter\n\n"), "" }, };/* xxx search for subsystem and disk unit and replace all */ s_node n_select_add_hot_spare = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Select Disks to Create Hot Spares"), .header = { __("Type option, press Enter.\n"), __(" 1=Select\n\n"), "" } }; s_node n_select_remove_hot_spare = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Select Hot Spares to Delete"), .header = { __("Type option, press Enter.\n"), __(" 1=Select\n\n"), "" } }; s_node n_confirm_add_hot_spare = { .f_flags = (CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Confirm Create Hot Spare"), .header = { __("ATTENTION: Existing data on these disks " "will not be preserved.\n\n"), __("Press Enter to continue.\n"), __(" q=Cancel to return and change your choice.\n\n"), "" }, }; s_node n_confirm_remove_hot_spare = { .f_flags = (CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Confirm Delete Hot Spare"), .header = { __("ATTENTION: Selected disks will no longer be available " "as hot spare devices.\n\n"), __("Press Enter to continue.\n"), __(" q=Cancel to return and change your choice.\n\n"), "" } }; struct screen_opts disk_unit_recovery_opt[] = { {concurrent_add_device, "1", __("Concurrent add device")}, {concurrent_remove_device, "2", __("Concurrent remove device")}, {init_device, "3", __("Initialize and format disk")}, {reclaim_cache, "4", __("Reclaim IOA cache storage")}, {raid_rebuild, "5", __("Rebuild disk unit data")}, {raid_resync, "6", __("Force RAID Consistency Check")}, {battery_maint, "7", __("Work with resources containing cache battery packs")}, {path_status, "8", __("Display SAS path status")}, {enclosures_maint, "9", __("Work with disk enclosures")} }; s_node n_disk_unit_recovery = { .rc_flags = (EXIT_FLAG | CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG), .num_opts = NUM_OPTS(disk_unit_recovery_opt), .options = &disk_unit_recovery_opt[0], .title = __("Work with Disk Unit Recovery") }; #define IPR_CONC_REMOVE 1 #define IPR_CONC_ADD 2 #define IPR_VERIFY_CONC_REMOVE 3 #define IPR_VERIFY_CONC_ADD 4 #define IPR_WAIT_CONC_REMOVE 5 #define IPR_WAIT_CONC_ADD 6 #define IPR_CONC_IDENTIFY 7 s_node n_concurrent_remove_device = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Concurrent Device Remove"), .header = { __("Choose a single location for remove operations\n"), __(" 1=Select\n\n"), "" } }; s_node n_concurrent_add_device = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Concurrent Device Add"), .header = { __("Choose a single location for add operations\n"), __(" 1=Select\n\n"), "" } }; s_node n_verify_conc_remove = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Verify Device Concurrent Remove"), .header = { __("Verify the selected device for concurrent remove operations\n\n"), __("Press Enter when verification complete and ready to continue\n"), __(" q=Cancel to return and change your choice.\n\n"), "" } }; s_node n_verify_conc_add = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Verify Device Concurrent Add"), .header = { __("Verify the selected device for concurrent add operations\n\n"), __("Press Enter when verification complete and ready to continue\n"), __(" q=Cancel to return and change your choice.\n\n"), "" } }; s_node n_wait_conc_remove = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Complete Device Concurrent Remove"), .header = { __("Remove selected device\n\n"), __("Press Enter when selected device has been removed\n"), __(" q=Cancel to return and change your choice.\n\n"), "" } }; s_node n_wait_conc_add = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Complete Device Concurrent Add"), .header = { __("Add selected device\n\n"), __("Press Enter when selected device has been installed\n"), __(" q=Cancel to return and change your choice.\n\n"), "" } }; struct screen_opts init_device_opt[] = { {confirm_init_device, "\n"} }; s_node n_af_init_device = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(init_device_opt), .options = &init_device_opt[0], .title = __("Select Disks to format for RAID Function"), .header = { __("Type option, press Enter.\n"), __(" 1=Select\n\n"), "" } }; s_node n_jbod_init_device = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(init_device_opt), .options = &init_device_opt[0], .title = __("Select Disks to format for JBOD Function (512)"), .header = { __("Type option, press Enter.\n"), __(" 1=Select\n\n"), "" } }; s_node n_init_device = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(init_device_opt), .options = &init_device_opt[0], .title = __("Select Disks for Initialize and Format"), .header = { __("Type option, press Enter.\n"), __(" 1=Select\n\n"), "" } }; struct screen_opts confirm_inits_opt[] = { {send_dev_inits, "c"} }; s_node n_confirm_init_device = { .f_flags = (CONFIRM_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(confirm_inits_opt), .options = &confirm_inits_opt[0], .title = __("Confirm Initialize and Format Disks"), .header = { __("Press 'c' to confirm your choice for 1=Initialize and format.\n"), __(" q=Return to change your choice.\n\n"), "" } }; s_node n_dev_init_complete = { .title = __("Initialize and Format Status"), .body = __("You selected to initialize and format a disk") }; struct screen_opts reclaim_cache_opts[] = { {confirm_reclaim, "\n"} }; s_node n_reclaim_cache = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG), .num_opts = NUM_OPTS(reclaim_cache_opts), .options = &reclaim_cache_opts[0], .title = __("Reclaim IOA Cache Storage"), .header = { __("Select the IOA to reclaim IOA cache storage.\n"), __("ATTENTION: Proceed with this function only if directed " "to from a service procedure. Data in the IOA cache " "will be discarded.\n\n"), __("Type choice, press Enter.\n"), __(" 1=Reclaim IOA cache storage.\n\n"), "" } }; struct screen_opts confirm_reclaim_opt[] = { {reclaim_warning, "c"} }; s_node n_confirm_reclaim = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(confirm_reclaim_opt), .options = &confirm_reclaim_opt[0], .title = __("Confirm Reclaim IOA Cache Storage"), .header = { __("The disks that may be affected by the function are displayed.\n"), __("ATTENTION: Proceed with this function only if directed to from a " "service procedure. Data in the IOA cache will be discarded. " "Filesystem corruption may result on the system.\n\n"), __("Press c=Confirm to reclaim cache storage.\n"), __(" q=Cancel to return to change your choice.\n\n"), "" } }; struct screen_opts confirm_reclaim_warning_opt[] = { {reclaim_result, "s"} }; s_node n_confirm_reclaim_warning = { .f_flags = (CONFIRM_REC_FLAG | CANCEL_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(confirm_reclaim_warning_opt), .options = &confirm_reclaim_warning_opt[0], .title = __("Confirm Reclaim IOA Cache Storage"), .header = { __("ATTENTION!!! ATTENTION!!! ATTENTION!!! ATTENTION!!!\n"), __("ATTENTION: Proceed with this function only if directed to from a " "service procedure. Data in the IOA cache will be discarded. " "This data loss may or may not be detected by the host operating " "system. Filesystem corruption may result on the system.\n\n"), __("Press 's' to continue.\n\n"), "" } }; s_node n_reclaim_result = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Reclaim IOA Cache Storage Results") }; struct screen_opts raid_rebuild_opt[] = { {confirm_raid_rebuild, "\n"} }; s_node n_raid_rebuild = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(raid_rebuild_opt), .options = &raid_rebuild_opt[0], .title = __("Rebuild Disk Unit Data"), .header = { __("Select the disks to be rebuilt\n\n"), __("Type choice, press Enter.\n"), __(" 1=Rebuild\n\n"), "" } }; s_node n_raid_rebuild_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Rebuild Disk Unit Data Failed"), .header = { __("There are no disks eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are no disks that need to be rebuilt.\n"), __("o The disk that needs to be rebuilt is not at the right " "location. Examine the 'message log' for the exposed unit " "and make sure that the replacement unit is at the correct " "location.\n"), __("o Not all disks attached to an IOA have reported to the " "system. Retry the operation.\n"), __("o The disk is not supported for the requested operation.\n"), "" } }; s_node n_confirm_raid_rebuild = { .f_flags = (CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Confirm Rebuild Disk Unit Data"), .header = { __("Rebuilding the disk unit data may take several " "minutes for each disk selected.\n\n"), __("Press Enter to confirm having the data rebuilt.\n"), __(" q=Cancel to return and change your choice.\n\n"), "" }, }; s_node n_raid_resync_complete = { .title = __("Force RAID Consistency Check Status"), .body = __("You selected to force a RAID consistency check") }; struct screen_opts raid_resync_opt[] = { {confirm_raid_resync, "\n"} }; s_node n_raid_resync = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(raid_resync_opt), .options = &raid_resync_opt[0], .title = __("Force RAID Consistency Check"), .header = { __("Select the arrays to be checked\n\n"), __("Type choice, press Enter.\n"), __(" 1=Check\n\n"), "" } }; s_node n_raid_resync_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Force RAID Consistency Check Failed"), .header = { __("There are no arrays eligible for the selected operation " "due to one or more of the following reasons:\n\n"), __("o There are no disk arrays in the system.\n"), __("o An IOA is in a condition that makes the disks attached to " "it read/write protected. Examine the kernel messages log " "for any errors that are logged for the IO subsystem " "and follow the appropriate procedure for the reference code to " "correct the problem, if necessary.\n"), __("o Not all disks attached to an advanced function IOA have " "reported to the system. Retry the operation.\n"), __("o The disks are missing.\n"), "" } }; s_node n_confirm_raid_resync = { .f_flags = (CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Confirm Force RAID Consistency Check"), .header = { __("Forcing a consistency check on a RAID array may take " "a long time, depending on current system activity.\n\n"), __("Press Enter to confirm.\n"), __(" q=Cancel to return and change your choice.\n\n"), "" }, }; struct screen_opts configure_af_device_opt[] = { {confirm_init_device, "\n"} }; struct screen_opts battery_maint_opt[] = { {battery_fork, "\n"} }; s_node n_battery_maint = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG), .num_opts = NUM_OPTS(battery_maint_opt), .options = &battery_maint_opt[0], .title = __("Work with Resources Containing Cache Battery Packs"), .header = { __("Type options, press Enter\n"), __(" 1=Display battery information\n"), __(" 2=Force battery pack into error state\n"), __(" 3=Start IOA cache\n\n"), "" } }; struct screen_opts confirm_force_battery_error_opt[] = { {force_battery_error, "c"} }; s_node n_confirm_force_battery_error = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(confirm_force_battery_error_opt), .options = &confirm_force_battery_error_opt[0], .title = __("Force Battery Packs Into Error State"), .header = { __("ATTENTION: This service function should be run only " "under the direction of the IBM Hardware Service Support\n\n"), __("You have selected to force a cache batter error on an IOA\n\n"), __("You will have to replace the Cache Battery Pack in each selected " "IOA to resume normal operations.\n\n"), __("System performance could be significantly degraded until the cache " "battery packs are replaced on the selected IOAs.\n\n"), __(" c=Continue to force the following battery packs into an error state\n\n"), "" }, }; struct screen_opts confirm_start_cache_opt[] = { {enable_battery, "c"} }; s_node n_confirm_start_cache = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(confirm_start_cache_opt), .options = &confirm_start_cache_opt[0], .title = __("Start IOA cache after concurrently replacing battery pack"), .header = { __("ATTENTION: This service function should be run only " "under the direction of the IBM Hardware Service Support\n\n"), __("You have selected to start the IOA cache after concurrently replacing the battery pack\n\n"), __(" c=Continue to start IOA cache\n\n"), "" }, }; s_node n_show_battery_info = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Battery Information") }; struct screen_opts enclosures_maint_opt[] = { {enclosures_fork, "\n"} }; s_node n_enclosures_maint = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(enclosures_maint_opt), .options = &enclosures_maint_opt[0], .title = __("Work with disk enclosures"), .header = { __("Type options, press Enter\n"), __(" 1=Display disk enclosure details\n"), __(" 2=Suspend disk enclosure path\n"), __(" 3=Resume disk enclosure path\n\n"), "" } }; struct screen_opts confirm_suspend_disk_enclosure_opt[] = { {ipr_suspend_disk_enclosure, "c"} }; s_node n_confirm_suspend_disk_enclosure = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(confirm_suspend_disk_enclosure_opt), .options = &confirm_suspend_disk_enclosure_opt[0], .title = __("Suspend Disk Enclosure"), .header = { __("ATTENTION: This service function should be run only " "under the direction of the IBM Hardware Service Support\n\n"), __(" c=Continue to issue suspend enclosure command\n\n"), "" }, }; struct screen_opts confirm_resume_disk_enclosure_opt[] = { {ipr_resume_disk_enclosure, "c"} }; s_node n_confirm_resume_disk_enclosure = { .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(confirm_resume_disk_enclosure_opt), .options = &confirm_resume_disk_enclosure_opt[0], .title = __("Resume Disk Enclosure"), .header = { __("ATTENTION: This service function should be run only " "under the direction of the IBM Hardware Service Support\n\n"), __(" c=Continue to issue resume enclosure command\n\n"), "" }, }; s_node n_show_enclosure_info = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Hardware detail Information") }; struct screen_opts path_status_opt[] = { {path_details, "\n"} }; s_node n_path_status = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(path_status_opt), .options = &path_status_opt[0], .title = __("Display SAS Path Status"), .header = { __("Type option, press Enter.\n"), __(" 1=Display SAS Path routing details\n\n"), "" } }; s_node n_path_details = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | FWD_FLAG), .title = __("Display SAS Path Details"), .header = {__("\n\n"), ""} }; struct screen_opts bus_config_opt[] = { {change_bus_attr, "\n"} }; s_node n_bus_config = { .rc_flags = (CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(bus_config_opt), .options = &bus_config_opt[0], .title = __("Work with SCSI Bus Configuration"), .header = { __("Select the adapter to change scsi bus attribute.\n\n"), __("Type choice, press Enter.\n"), __(" 1=change scsi bus attribute\n\n"), "" } }; s_node n_bus_config_fail = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG), .title = __("Work with SCSI Bus Configuration Failed"), .header = { __("There are no SCSI Buses eligible for the selected operation. " "None of the installed adapters support the requested " "operation.\n"), "" } }; struct screen_opts change_bus_attr_opt[] = { {NULL, "c"}, {confirm_change_bus_attr, "\n"} }; s_node n_change_bus_attr = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG | FWD_FLAG | MENU_FLAG), .num_opts = NUM_OPTS(change_bus_attr_opt), .options = &change_bus_attr_opt[0], .title = __("Change SCSI Bus Configuration"), .header = { __("Current bus configurations are shown. To change " "setting hit 'c' for options menu. Highlight " "desired option then hit Enter.\n"), __(" c=Change Setting\n\n"), "" } }; struct screen_opts confirm_change_bus_attr_opt[] = { {NULL, "c"} }; s_node n_confirm_change_bus_attr = { .f_flags = (CONFIRM_FLAG | EXIT_FLAG | CANCEL_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(confirm_change_bus_attr_opt), .options = &confirm_change_bus_attr_opt[0], .title = __("Confirm Change SCSI Bus Configuration"), .header = { __("Confirming this action will result in the following" "configuration to become active.\n"), __(" c=Confirm Change SCSI Bus Configration\n\n"), "" } }; struct screen_opts driver_config_opt[] = { {change_driver_config, "\n"} }; s_node n_driver_config = { .rc_flags = (CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(driver_config_opt), .options = &driver_config_opt[0], .title = __("Adapter Driver Configuration"), .header = { __("Select adapter to change driver configuration\n\n"), __("Type choice, press Enter\n"), __(" 1=change driver configuration\n\n"), "" } }; s_node n_change_driver_config = { .f_flags = (ENTER_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Change Driver Configuration"), .header = { __("Current Driver configurations " "are shown. To change setting, type in new " "value then hit Enter\n\n"), "" } }; struct screen_opts disk_config_opt[] = { {change_disk_config, "\n"} }; s_node n_disk_config = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(disk_config_opt), .options = &disk_config_opt[0], .title = __("Change Disk Configuration"), .header = { __("Type option, press Enter.\n"), __(" 1=Change Disk Configuration\n\n"), "" } }; struct screen_opts change_disk_attr_opt[] = { {NULL, "c"}, {NULL, "\n"} }; s_node n_change_disk_config = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG | FWD_FLAG | MENU_FLAG), .num_opts = NUM_OPTS(change_disk_attr_opt), .options = &change_disk_attr_opt[0], .title = __("Change Configuration of Disk"), .header = { __("Current Disk configurations are shown. To change " "setting hit 'c' for options menu. Highlight " "desired option then hit Enter.\n"), __(" c=Change Setting\n\n"), "" } }; struct screen_opts ioa_config_opt[] = { {change_ioa_config, "\n"} }; s_node n_ioa_config = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG | TOGGLE_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(ioa_config_opt), .options = &ioa_config_opt[0], .title = __("Change Adapter Configuration"), .header = { __("Type option, press Enter.\n"), __(" 1=Change Adapter Configuration\n\n"), "" } }; struct screen_opts change_ioa_attr_opt[] = { {NULL, "c"}, {NULL, "\n"} }; s_node n_change_ioa_config = { .f_flags = (ENTER_FLAG | EXIT_FLAG | CANCEL_FLAG | FWD_FLAG | MENU_FLAG), .num_opts = NUM_OPTS(change_ioa_attr_opt), .options = &change_ioa_attr_opt[0], .title = __("Change Configuration of Adapter"), .header = { __("Current Adapter configurations are shown. To change " "setting hit 'c' for options menu. Highlight " "desired option then hit Enter.\n"), __(" c=Change Setting\n\n"), "" } }; struct screen_opts download_ucode_opt[] = { {choose_ucode, "\n"} }; s_node n_download_ucode = { .rc_flags = (CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG | FWD_FLAG | TOGGLE_FLAG), .num_opts = NUM_OPTS(download_ucode_opt), .options = &download_ucode_opt[0], .title = __("Download Microcode"), .header = { __("Select the adapter(s) to download microcode\n\n"), __("Type choice, press Enter.\n"), __(" 1=device to download microcode\n\n"), "" } }; s_node n_choose_ucode = { .rc_flags = (CANCEL_FLAG), .f_flags = (CANCEL_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Choose Microcode Image"), .header = { __("Select the microcode image to download\n\n"), __("Type choice, press Enter.\n"), __(" 1=download microcode\n\n"), "OPT Version Date Image File\n", "--- --------------- -------- --------------------------\n", "" } }; s_node n_confirm_download_ucode = { .rc_flags = (CANCEL_FLAG), .f_flags = (CANCEL_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Confirm Microcode Download"), .header = { __("ATTENTION: System performance may be affected during " "the microcode download process\n\n"), __("Press Enter to continue.\n"), __(" q=Cancel to return and change your choice.\n\n"), "OPT Version Date Image File\n", "--- --------------- -------- --------------------------\n", "" } }; s_node n_download_ucode_in_progress = { .rc_flags = (CANCEL_FLAG), .f_flags = (CANCEL_FLAG | FWD_FLAG), .num_opts = NUM_OPTS(null_opt), .options = &null_opt[0], .title = __("Microcode Download In Progress"), .header = { __("Please wait for the microcode update to complete\n"), __("This process may take several minutes\n"), "" } }; struct screen_opts log_menu_opt[] = { {ibm_storage_log_tail, "1", __("View most recent ipr error messages")}, {ibm_storage_log, "2", __("View ipr error messages")}, {kernel_log, "3", __("View all kernel error messages")}, {iprconfig_log, "4", __("View iprconfig error messages")}, {kernel_root, "5", __("Set root kernel message log directory")}, {set_default_editor, "6", __("Set default editor")}, {restore_log_defaults, "7", __("Restore defaults")}, {ibm_boot_log, "8", __("View ipr boot time messages")} }; s_node n_log_menu = { .rc_flags = (EXIT_FLAG | CANCEL_FLAG | REFRESH_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG), .num_opts = NUM_OPTS(log_menu_opt), .options = &log_menu_opt[0], .title = __("Kernel Messages Log"), .header = { __("ATTENTION: Devices listed below are currently known to be zeroed. " "By exiting now, this information will be lost, resulting in potentially " "longer array creation times.\n\n"), __(" q=Cancel to return to iprconfig.\n"), __(" e=Exit iprconfig\n") } }; s_node n_exit_menu = { .rc_flags = (EXIT_FLAG | CANCEL_FLAG), .f_flags = (EXIT_FLAG | CANCEL_FLAG), .num_opts = NUM_OPTS(log_menu_opt), .options = &null_opt[0], .title = __("Confirm Exit") }; struct screen_opts kernel_root_opt[] = { {confirm_kernel_root, "\n"} }; s_node n_kernel_root = { .f_flags = (ENTER_FLAG), .num_opts = NUM_OPTS(kernel_root_opt), .options = &kernel_root_opt[0], .title = __("Kernel Messages Log Root Directory"), .header = { __("Enter new directory and press Enter\n\n"), "" } }; struct screen_opts confirm_kernel_root_opt[] = { {NULL, "c"}, }; s_node n_confirm_kernel_root = { .f_flags = (CONFIRM_FLAG | CANCEL_FLAG), .num_opts = NUM_OPTS(confirm_kernel_root_opt), .options = &confirm_kernel_root_opt[0], .title = __("Confirm Root Directory Change"), .header = { __("Press 'c' to confirm your new " "kernel root directory choice.\n\n"), __("To return to the log menu without " "changing the root directory, choose 'q'.\n\n"), "" } }; struct screen_opts set_default_editor_opt[] = { {confirm_set_default_editor, "\n"} }; s_node n_set_default_editor = { .f_flags = (ENTER_FLAG), .num_opts = NUM_OPTS(set_default_editor_opt), .options = &set_default_editor_opt[0], .title = __("Kernel Messages Editor"), .header = { __("Enter new editor command and press Enter\n\n"), "" } }; struct screen_opts confirm_set_default_editor_opt[] = { {NULL, "c"} }; s_node n_confirm_set_default_editor = { .f_flags = (CONFIRM_FLAG | CANCEL_FLAG), .num_opts = NUM_OPTS(confirm_set_default_editor_opt), .options = &confirm_set_default_editor_opt[0], .title = __("Confirm Editor Change"), .header = { __("Press 'c' to confirm your editor choice\n\n"), __("To return to the log menu without " "changing the editor, choose 'q'.\n\n"), "" } }; const char *screen_status[] = { /* 0 */ "", /* 1 */ "", /* 2 */ __("Invalid option specified"), /* 3 */ __("Screen paged up"), /* 4 */ __("Already at top"), /* 5 */ __("Screen paged down"), /* 6 */ __("Already at bottom"), /* 7 */ "", /* 8 */ "", /* 9 */ "", /* 10 */ __("No devices to show details for"), /* 11 */ "", /* 12 */ "", /* 13 */ "", /* 14 */ "", /* 15 */ __("The selection is not valid."), /* 16 */ __("Error. More than one device was selected."), /* 17 */ __("Invalid option. No devices selected."), /* 18 */ __("Disk array successfully created."), /* 19 */ __("Create disk array failed."), /* 20 */ __("Delete disk array failed."), /* 21 */ __("Disk array successfully deleted."), /* 22 */ __("Create disk array failed - can not mix SSDs and HDDs."), /* 23 */ "", /* 24 */ "", /* 25 */ __("Error: number of devices selected must be a multiple of %d"), /* 26 */ __("Add device failed"), /* 27 */ __("Add device completed successfully"), /* 28 */ __("Rebuild started, view Disk Array Status Window for rebuild progress"), /* 29 */ __("Rebuild failed"), /* 30 */ __("Device concurrent maintenance failed"), /* 31 */ __("Device concurrent maintenance failed, device in use"), /* 32 */ __("Device concurrent maintenance completed successfully"), /* 33 */ __("No units available for initialize and format"), /* 34 */ __("Initialize and format completed successfully"), /* 35 */ __("Initialize and format failed"), /* 36 */ __("Reclaim IOA Cache Storage completed successfully"), /* 37 */ __("Reclaim IOA Cache Storage failed"), /* 38 */ __("No Reclaim IOA Cache Storage is necessary"), /* 39 */ __("No Reclaim IOA Cache Storage performed"), /* 40 */ __("Rebuild started, view Disk Array Status Window for rebuild progress"), /* 41 */ __("Rebuild failed"), /* 42 */ __("The selected battery packs have successfully been placed into an error state."), /* 43 */ __("Failed to force the selected battery packs into an error state."), /* 44 */ __("No configured resources contain a cache battery pack."), /* 45 */ __("Change SCSI Bus configurations completed successfully"), /* 46 */ __("Change SCSI Bus configurations failed"), /* 47 */ __("Change device driver configurations completed successfully"), /* 48 */ __("Change device driver configurations failed"), /* 49 */ __("No units available for initialize and format"), /* 50 */ __("Initialize and format completed successfully"), /* 51 */ __("Initialize and format failed"), /* 52 */ __("No devices available for the selected hot spare operation"), /* 53 */ __("Hot spare successfully created"), /* 54 */ __("Hot spare successfully deleted"), /* 55 */ __("Failed to create hot spare"), /* 56 */ __("Failed to delete hot spare"), /* 57 */ __("Successfully changed device driver configuration"), /* 58 */ __("Failed to change device driver configuration"), /* 59 */ __("Invalid directory"), /* 60 */ __("Root directory changed to %s"), /* 61 */ __("Root directory unchanged"), /* 62 */ __("Editor changed to %s"), /* 63 */ __("Editor unchanged"), /* 64 */ __("Default log values restored"), /* 65 */ __("Editor returned %d. Try setting the default editor."), /* 66 */ __("Failed to change disk configuration."), /* 67 */ __("Microcode Download failed."), /* 68 */ __("Failed to enable IOA cache."), /* 69 */ __("Invalid number of devices selected."), /* 70 */ __("Failed to start IOA cache."), /* 71 */ __("IOA cache started successfully."), /* 72 */ __("Selected battery packs successfully forced into an error state."), /* 73 */ __("Force RAID Consistency check failed"), /* 74 */ __("RAID Consistency check successful"), /* 75 */ __("Failed to read error log. Try setting root kernel message log directory."), /* 76 */ __("No SAS disks available"), /* 77 */ __("Too many disks were selected. The maximum is %d."), /* 78 */ __("Too few disks were selected. The minimum is %d."), /* 79 */ __("Migrate Array Protection completed successfully."), /* 80 */ __("Migrate Array Protection failed."), /* 81 */ __("Selected disk enclosure was suspended successfully."), /* 82 */ __("Failed to suspend disk enclosure"), /* 83 */ __("Disk enclosure is already in suspend state"), /* 84 */ __("Disk enclosure is already in active state"), /* 85 */ __("Selected disk enclosure was resumed successfully."), /* 86 */ __("Failed to resume disk enclosure"), /* 87 */ __("Can not suspend an expander from the RAID controller with the same serial number"), /* 88 */ __("Incorrect device type specified. Please specify a valid disk enclosure to suspend"), /* 89 */ __("Incorrect device type specified. Please specify a valid disk enclosure to resume"), /* 90 */ __("Selected disk enclosure is in Unknown state. Please check your hardware support"), /* 91 */ __("Create disk array failed - can not mix 5XX and 4K disks."), /* NOTE: 127 maximum limit */ }; /* TODO - replace constants in iprconfig.c with the following enums/defines */ enum { RC_0_Success = 0, RC_1_Blank, RC_2_Invalid_Option, RC_3_Screen_Up, RC_4_At_Top, RC_5_Screen_Down, RC_6_At_Bottom, RC_7_Blank, RC_8_Blank, RC_9_Blank, RC_10_No_Devices, RC_11_Blank, RC_12_Blank, RC_13_Blank, RC_14_Blank, RC_15_Invalid_Selection, RC_16_More_Than_One_Dev, RC_17_Invalid_Option, RC_18_Array_Created, RC_19_Create_Fail, RC_20_Delete_Fail, RC_21_Delete_Success, RC_22_Mixed_Block_Dev_Classes, RC_23_Blank, RC_24_Blank, RC_25_Devices_Multiple, RC_26_Include_Fail, RC_27_Include_Success, RC_28_Rebuild_Started, RC_29_Rebuild_Fail, RC_30_Maint_Fail, RC_31_Maint_Fail_In_Use, RC_32_Maint_Success, RC_33_No_Units, RC_34_Init_Format_Success, RC_35_Init_Format_Fail, RC_36_Reclaim_Cache_Success, RC_37_Reclaim_Cache_Fail, RC_38_No_Reclaim_Needed, RC_39_No_Reclaim_Performed, RC_40_Rebuild_Started, RC_41_Rebuild_Failed, RC_42_Battery_Err_State_Success, RC_43_Battery_Err_State_Fail, RC_44_No_Battery_Pack, RC_45_Change_Bus_Conf_Success, RC_46_Change_Bus_Conf_Fail, RC_47_Change_Driver_Conf_Success, RC_48_Change_Driver_Conf_Fail, RC_49_No_Units_Available, RC_50_Init_Format_Success, RC_51_Init_Format_Fail, RC_52_No_Devices_Available, RC_53_Hot_Spare_Created, RC_54_Hot_Spare_Deleted, RC_55_Failed_Create, RC_56_Failed_Delete, RC_57_Change_Driver_Conf_Success, RC_58_Change_Driver_Conf_Fail, RC_59_Invalid_Dir, RC_60_Root_Changed, RC_61_Root_Unchanged, RC_62_Editor_Changed, RC_63_Editor_Unchanged, RC_64_Log_Restored, RC_65_Set_Default_Editor, RC_66_Disk_Conf_Fail, RC_67_uCode_Download_Fail, RC_68_Cache_Enable_Fail, RC_69_Invalid_Number_Devs, RC_70_Cache_Start_Fail, RC_71_Cache_Start_Success, RC_72_Battery_Err_State, RC_73_Force_Check_Fail, RC_74_Check_Success, RC_75_Failed_Read_Err_Log, RC_76_No_SAS_Disks, RC_77_Too_Many_Disks, RC_78_Too_Few_Disks, RC_79_Migrate_Prot_Success, RC_80_Migrate_Prot_Fail, RC_81_Suspended_Success, RC_82_Suspended_Fail, RC_83_Enclosure_Is_Suspend, RC_84_Enclosure_Is_Active, RC_85_Enclosure_Resume_Success, RC_86_Enclosure_Resume_Fail, RC_87_No_Suspend_Same_Seri_Num, RC_88_Invalid_Dev_For_Suspend, RC_89_Invalid_Dev_For_Resume, RC_90_Enclosure_Is_Unknown, RC_91_Mixed_Logical_Blk_Size, /* NOTE: 127 maximum limit */ }; #endif /* iprconfig_h */ iprutils/init.d/0000755000000000000000000000000012275251125012616 5ustar rootrootiprutils/init.d/iprupdate0000644000000000000000000000357412275251125014547 0ustar rootroot#!/bin/sh # Copyright (C) 2004 International Business Machines Corporation and others. # All Rights Reserved. This program and the accompanying # materials are made available under the terms of the # Common Public License v1.0 which accompanies this distribution. # # Author: Brian King # # iprupdate # # System startup script for the ipr microcode update facility # ### BEGIN INIT INFO # Provides: iprupdate # Required-Start: $local_fs # Should-Start: $remote_fs $syslog # Required-Stop: $local_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start the iprupdate utility # Description: Start the iprupdate utility ### END INIT INFO IPRUPDATE=/sbin/iprupdate test -x $IPRUPDATE || exit 5 . /lib/lsb/init-functions start() { echo "Checking ipr microcode levels" if [ ! -d /sys/class/scsi_generic ] ; then modprobe sg fi start_daemon $IPRUPDATE --daemon RETVAL=$? if [ $RETVAL -eq 0 ]; then echo -n "Completed ipr microcode updates" log_success_msg " " else echo -n "Completed ipr microcode updates" log_failure_msg " " fi return $RETVAL } stop() { echo -n "Shutting down ipr update daemon" killproc $IPRUPDATE RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg " " else log_failure_msg " " fi return $RETVAL } restart() { $0 stop $0 start return $RETVAL } reload() { $0 stop $0 start return $RETVAL } status() { echo -n "Checking for ipr update daemon: " pidofproc $IPRUPDATE >& /dev/null RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg "running" else log_failure_msg "unused" fi return $RETVAL } case "$1" in start) start ;; stop) stop ;; restart) restart ;; reload) reload ;; status) status ;; *) echo "Usage: $0 {start|stop|status|restart|reload}" exit 1 ;; esac iprutils/init.d/iprdump0000644000000000000000000000350312275251125014222 0ustar rootroot#!/bin/sh # Copyright (C) 2004 International Business Machines Corporation and others. # All Rights Reserved. This program and the accompanying # materials are made available under the terms of the # Common Public License v1.0 which accompanies this distribution. # # Author: Brian King # # iprdump # # System startup script for the ipr dump facility # ### BEGIN INIT INFO # Provides: iprdump # Required-Start: $local_fs iprinit # Should-Start: $remote_fs $syslog # Required-Stop: $local_fs $syslog iprinit # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start the ipr dump daemon # Description: Start the ipr dump daemon ### END INIT INFO IPRDUMP=/sbin/iprdump test -x $IPRDUMP || exit 5 . /lib/lsb/init-functions start() { echo -n "Starting ipr dump daemon" if [ ! -d /sys/class/scsi_generic ] ; then modprobe sg fi start_daemon $IPRDUMP --daemon RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg " " else log_failure_msg " " fi return $RETVAL } stop() { echo -n "Shutting down ipr dump daemon" killproc $IPRDUMP RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg " " else log_failure_msg " " fi return $RETVAL } restart() { $0 stop $0 start return $RETVAL } reload() { echo -n "Reload ipr dump daemon" $0 stop && $0 start return $RETVAL } status() { echo -n "Checking for iprdump daemon: " pidofproc $IPRDUMP >& /dev/null RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg "running" else log_failure_msg "unused" fi return $RETVAL } case "$1" in start) start ;; stop) stop ;; restart) restart ;; reload) reload ;; status) status ;; *) echo "Usage: $0 {start|stop|status|restart|reload}" exit 1 ;; esac iprutils/init.d/iprha0000644000000000000000000000275512275251125013655 0ustar rootroot#!/bin/sh # Copyright (C) 2007 International Business Machines Corporation and others. # All Rights Reserved. This program and the accompanying # materials are made available under the terms of the # Common Public License v1.0 which accompanies this distribution. # # Author: Brian King # # iprha # # System startup script for the ipr HA facility # ### BEGIN INIT INFO # Provides: iprha # Required-Start: $local_fs # Should-Start: $remote_fs $syslog # Required-Stop: $local_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Set all ipr adapters to be preferred primary adapter # Description: Set all ipr adapters to be preferred primary adapter. # This script should be started on the "primary" system in a high # availability configuration for improved ipr disk performance. ### END INIT INFO IPRCONFIG=/sbin/iprconfig test -x $IPRCONFIG || exit 5 . /lib/lsb/init-functions start() { echo -n "Enabling ipr primary adapter mode" $IPRCONFIG -c set-all-primary RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg " " else log_failure_msg " " fi return $RETVAL } stop() { echo -n "Disabling ipr primary adapter mode" $IPRCONFIG -c set-all-secondary RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg " " else log_failure_msg " " fi return $RETVAL } case "$1" in start) start ;; stop) stop ;; *) echo "Usage: $0 {start|stop}" exit 1 ;; esac iprutils/init.d/iprinit0000644000000000000000000000355212275251125014224 0ustar rootroot#!/bin/sh # Copyright (C) 2004 International Business Machines Corporation and others. # All Rights Reserved. This program and the accompanying # materials are made available under the terms of the # Common Public License v1.0 which accompanies this distribution. # # Author: Brian King # # iprinit # # System startup script for the ipr init facility # ### BEGIN INIT INFO # Provides: iprinit # Required-Start: $local_fs # Should-Start: $remote_fs $syslog # Required-Stop: $local_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start the ipr init daemon # Description: Start the ipr initialization daemon ### END INIT INFO IPRINIT=/sbin/iprinit test -x $IPRINIT || exit 5 . /lib/lsb/init-functions start() { echo -n "Starting ipr initialization daemon" if [ ! -d /sys/class/scsi_generic ] ; then modprobe sg fi start_daemon $IPRINIT --daemon RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg " " else log_failure_msg " " fi return $RETVAL } stop() { echo -n "Shutting down ipr initialization daemon" killproc $IPRINIT RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg " " else log_failure_msg " " fi return $RETVAL } restart() { $0 stop $0 start return $RETVAL } reload() { echo -n "Reload ipr initialization daemon" $0 stop && $0 start return $RETVAL } status() { echo -n "Checking for ipr initialization daemon: " pidofproc $IPRINIT >& /dev/null RETVAL=$? if [ $RETVAL -eq 0 ]; then log_success_msg "running" else log_failure_msg "unused" fi return $RETVAL } case "$1" in start) start ;; stop) stop ;; restart) restart ;; reload) reload ;; status) status ;; *) echo "Usage: $0 {start|stop|status|restart|reload}" exit 1 ;; esac iprutils/.git/0000755000000000000000000000000012275251164012275 5ustar rootrootiprutils/.git/logs/0000755000000000000000000000000012275251125013236 5ustar rootrootiprutils/.git/logs/refs/0000755000000000000000000000000012275251125014175 5ustar rootrootiprutils/.git/logs/refs/heads/0000755000000000000000000000000012275251164015264 5ustar rootrootiprutils/.git/logs/refs/heads/master0000644000000000000000000000030712275251125016477 0ustar rootroot0000000000000000000000000000000000000000 77b15d9d0d5974869ccd802febf7d9e6dc91c786 root 1391809109 -0600 clone: from ssh://wenxiong@git.code.sf.net/p/iprdd/iprutils iprutils/.git/logs/refs/heads/rel-2-4-00000644000000000000000000000027612275251164016433 0ustar rootroot0000000000000000000000000000000000000000 406051a5fd93b916d31464173d938d4c1f49b401 root 1391809140 -0600 branch: Created from refs/remotes/origin/rel-2-4-0 iprutils/.git/logs/HEAD0000644000000000000000000000057412275251164013673 0ustar rootroot0000000000000000000000000000000000000000 77b15d9d0d5974869ccd802febf7d9e6dc91c786 root 1391809109 -0600 clone: from ssh://wenxiong@git.code.sf.net/p/iprdd/iprutils 77b15d9d0d5974869ccd802febf7d9e6dc91c786 406051a5fd93b916d31464173d938d4c1f49b401 root 1391809140 -0600 checkout: moving from master to rel-2-4-0 iprutils/.git/branches/0000755000000000000000000000000012275251105014055 5ustar rootrootiprutils/.git/refs/0000755000000000000000000000000012275251125013231 5ustar rootrootiprutils/.git/refs/remotes/0000755000000000000000000000000012275251125014707 5ustar rootrootiprutils/.git/refs/remotes/origin/0000755000000000000000000000000012275251125016176 5ustar rootrootiprutils/.git/refs/remotes/origin/HEAD0000644000000000000000000000004012275251125016614 0ustar rootrootref: refs/remotes/origin/master iprutils/.git/refs/tags/0000755000000000000000000000000012275251105014165 5ustar rootrootiprutils/.git/refs/heads/0000755000000000000000000000000012275251164014320 5ustar rootrootiprutils/.git/refs/heads/master0000644000000000000000000000005112275251125015527 0ustar rootroot77b15d9d0d5974869ccd802febf7d9e6dc91c786 iprutils/.git/refs/heads/rel-2-4-00000644000000000000000000000005112275251164015456 0ustar rootroot406051a5fd93b916d31464173d938d4c1f49b401 iprutils/.git/packed-refs0000644000000000000000000001365312275251125014411 0ustar rootroot# pack-refs with: peeled 17692fd5a13e35a6d3a283fc2334a53ea07f342c refs/tags/start 44e4e925a9acb392006108b270f9ccd625f7b963 refs/tags/show ^b87d6d7d24db3cc53334d72951bb57b32af0330e b9b9152eb5600a8fa77d76707145ceaeec3da22c refs/tags/rel-2.3.18 ^77b15d9d0d5974869ccd802febf7d9e6dc91c786 1f290e5b212233899ebabd908a8767a07485fd31 refs/tags/rel-2-3-9 ^ebd1142d304ab9cb082fd4765cd572a781ccdd53 dca05e8116353b0c8664a36b341c83fdff4126d6 refs/tags/rel-2-3-8 ^ea98944a9a02d9bf45fcbddfdd17f0395b17c790 017d4e6a79e6b74796b36ff40f0c5400841515cb refs/tags/rel-2-3-7 ^a1bcd6711bd2062d7b02d8f59cd0ab50c62686a7 045d5bc6fcab6c30dfb88aec3a2a4117df9cab5a refs/tags/rel-2-3-6 ^a9a6e2607796cc8427ed93cec1ccfcbb8ba23af4 ac2a7d7eb375ab609dc155d82da2138cf38c9935 refs/tags/rel-2-3-5 ^8b15ab95745b9b57955f79de55efa229b88d71da a011831f6014e245417205ca6b5d9363c7646b8b refs/tags/rel-2-3-4 ^b8cb3b0911e40e8b5bb8adfda19901e39609bdc2 289037d6c2c7fb1874979ccc049d953cc9d5924e refs/tags/rel-2-3-3 ^bdd0238b395d81b426ebd75857745c696de0bab7 44e4e925a9acb392006108b270f9ccd625f7b963 refs/tags/rel-2-3-2 ^b87d6d7d24db3cc53334d72951bb57b32af0330e b1ddcd9697cd64a7c97cd27ee2eac0c6b7c8b138 refs/tags/rel-2-3-13 ^41a82b487901e4814d4a362b875c9b5e428bc5f7 d9c79306d8dc0ddd7ff48ec556903e8f10411f38 refs/tags/rel-2-3-12 ^44607dc8d7829fe22bc324e9572e30ee796ce793 f3b1677f6e344115135d383d2f20a9688ccf1ab1 refs/tags/rel-2-3-11 ^37690cdc9df1ecc135f7c159a9349afa5ed853cc b8efe554e5dd384bbdb0c229ab3e74536a3eff4c refs/tags/rel-2-3-10 ^8c94ac7a4f4d9c140ed3667d47e5f165add009ca 9c421c4667243dc574e8038415502f85078a2109 refs/tags/rel-2-3-1 ^3e09808a549cde65fc437742eac18be53a7de247 7d5943f85316b6e2a12083de7429d0b7e9a19d5a refs/tags/rel-2-3-0 ^0f513ca9b4736aeda8a0c6cac9440248d29637fe 6039126be72eeb94cbb628c0ec7e118b394ad917 refs/tags/rel-2-2-9 ba9d17e75801107b458096643acd2d615617a530 refs/tags/rel-2-2-8 20f1aeef9760b1453c9c825b3c9f5b1f6b34d3f5 refs/tags/rel-2-2-7 11d914fdcdff8975f095103a687a104fd8a6a0e2 refs/tags/rel-2-2-6 0a24e290e9474da97637d2987a2c87f1b55a75d8 refs/tags/rel-2-2-5 3c87a05c228d2474ed25cd9257020f5deb68d72b refs/tags/rel-2-2-4 60618de499405a7427c2fd83ccc95edaf8e8a6b9 refs/tags/rel-2-2-3 1011c13d7285f04b5d70a34eea5d29522ca11dc8 refs/tags/rel-2-2-21 ^59e71cda1cfbfe03b9b5470e51705f31864e0dcd 0c31a6773a158fd155fe451ffc002492f0716975 refs/tags/rel-2-2-20 ^8cca65671f8926bb9cd68c7aaf0fcb3469078843 6aef88a630e6e3dad0dd7c475a5e46184030c19b refs/tags/rel-2-2-2 5f05b7308ba621a5a5ea04c5f9f92f548fb94e56 refs/tags/rel-2-2-19 ^ed4a0f080166c2157171f493746580d3263d9fdc 1ba7fa46c301dc81a557f5a0ffe315dd0e7df61b refs/tags/rel-2-2-18 1add13d903d738489dddecb6845d3306333987bb refs/tags/rel-2-2-17 a66ddbc2b8f65aecf4020af63780d7f6cf471c7e refs/tags/rel-2-2-16 a6b87e63edef2c46ad960d0938a7b9343d51381f refs/tags/rel-2-2-15 7b2f28e47c13357fc0a848fd16559e55014d830c refs/tags/rel-2-2-14 335a13040d3f5e09db28f87abbf35adce0fb6579 refs/tags/rel-2-2-13 b439704c40480065ec4eb7d6831284c224c56dba refs/tags/rel-2-2-12 425b5770508800919d9bf2bd40472d1fd8fcc690 refs/tags/rel-2-2-11 edb6130e5c89dbe1c54fc6f021a173209a074539 refs/tags/rel-2-2-10 494e95957ccb1028ab2ca6bed74a4668f5cecb53 refs/tags/rel-2-2-0 51eef9f277e24e9ea169193ce9d75529dc1793bd refs/tags/rel-2-1-5 03125e70ac953c5d1de250262975461adf519d9a refs/tags/rel-2-1-4 cf5bc5ec5d80e6c7485c8291f1a406afd4821c43 refs/tags/rel-2-1-3 a7b62cde75fbd59c0b66a2d836ee8adfa3ddcf5e refs/tags/rel-2-1-2 73d9afa174f2572419cc8b1ba56d6696b68bf0bc refs/tags/rel-2-1-1 0855f112de044508e9a0b577ff6ed86d2800c2e5 refs/tags/rel-2-1-0 ff437e18f9b642b56280dc3f6db234b770401f02 refs/tags/rel-2-0-9 ad5b85323404b3385501e219314f5417819484df refs/tags/rel-2-0-8 7a9d059b33e4b4d54e5b438c13f2eab3e197cf81 refs/tags/rel-2-0-7 ad447a68cbf788cf258c16c9512a1c863a2bd449 refs/tags/rel-2-0-6 7f2e686db503120e0688166c85e1c91f89c0d6a7 refs/tags/rel-2-0-5 a85bb5965da6ac7a2c2c2794a151cd1ca5589c05 refs/tags/rel-2-0-4 82f650b3371b809ca06d52718e6899ddd1116340 refs/tags/rel-2-0-3 60c63b919e07f28858ee179cff12bc605f16ab08 refs/tags/rel-2-0-2-2 ad28a07002f271806c1bd1460b66e8a057f5a587 refs/tags/rel-2-0-2-1 0d87e657439549c60007c7bd070e532cfdf51bd1 refs/tags/rel-2-0-15-8 6ab484fe87765e27ae03125976fce5040ba495c7 refs/tags/rel-2-0-15-7 ed18281979189bcbb85ead3f71d1b24c0e55c8ec refs/tags/rel-2-0-15-6 a39fe1572823a8d440957a52040951f5b5222c2e refs/tags/rel-2-0-15-5 e0fd24f83e7d4f36663474b5bf12ccf03ab6f37b refs/tags/rel-2-0-15-4 86c79d0a13796f56d0f575a076ffbbe65398a92a refs/tags/rel-2-0-15-1 79209da7f7c72727f164f4a65c598031a8f9bf20 refs/tags/rel-2-0-15 5dd3d9ac1749c70d9df5d929337a366057bacfe3 refs/tags/rel-2-0-14-2 4c5eeb2ffca583dec129910f8480032dfd4b6845 refs/tags/rel-2-0-14-1 29d900e27edd3ae69270c52ce2fd8b5ddf598735 refs/tags/rel-2-0-14 c48ae3aa3079fe3dca0a2d816b88fec1e2fc9d71 refs/tags/rel-2-0-13-4 197c8ff0aef5319d552df9a3aa0d6314e8eeaa0e refs/tags/rel-2-0-13-3 db3f3598631fba6f3731f8889b6c679bcfeaca35 refs/tags/rel-2-0-13-2 efc82ca821b4dcb3d370cf0238dacd917c491aff refs/tags/rel-2-0-13-1 a3e57b048a6f5d65263996722bc5c23396de89eb refs/tags/rel-2-0-13 fc1cbfd870afe03709b1ca8fe12cbc6d03318630 refs/tags/rel-2-0-12 6ea6fcbeea7d1c805d5b1236948c883b15e32703 refs/tags/rel-2-0-11 30cad1be8944d95d433bb7309bf12abd4305afd0 refs/tags/rel-2-0-10 5fccb3809f3b77eeeba5f9e23567787565d58600 refs/tags/rel-2-0-0-1 47a9abf270ff65e8b4f3cdeb43f7fb2d0817f242 refs/tags/rel-1-0-4 eb76dc9684b8f82045d7a4144ded78485d41c3c8 refs/tags/rel-1-0-3-2 406051a5fd93b916d31464173d938d4c1f49b401 refs/remotes/origin/rel-2-4-0 51eef9f277e24e9ea169193ce9d75529dc1793bd refs/remotes/origin/rel-2-1-stable c48ae3aa3079fe3dca0a2d816b88fec1e2fc9d71 refs/remotes/origin/rel-2-0-stable 0d87e657439549c60007c7bd070e532cfdf51bd1 refs/remotes/origin/rel-2-0-15-stable 5dd3d9ac1749c70d9df5d929337a366057bacfe3 refs/remotes/origin/rel-2-0-14-stable 47a9abf270ff65e8b4f3cdeb43f7fb2d0817f242 refs/remotes/origin/rel-1-0-stable 1ba7fa46c301dc81a557f5a0ffe315dd0e7df61b refs/remotes/origin/origin 77b15d9d0d5974869ccd802febf7d9e6dc91c786 refs/remotes/origin/master 17692fd5a13e35a6d3a283fc2334a53ea07f342c refs/remotes/origin/iprutils iprutils/.git/index0000644000000000000000000000463012275251164013332 0ustar rootrootDIRCRRU8xRRU8xu-r'LWEHLICENSERRt1V1RRt1V1v =o1n;3MakefileRRU8xRRU8xS֠(܊wREADMERRU8xRRU8x5!#4뜮# debug/MakefileRRU8xRRU8x 0y- FgbGdebug/ipraliases.cRRU8xRRU8x^lY?ޞYRo~debug/iprcache.cRRU8xRRU8xLmqτ8KEdebug/iprluns.cRRU8xRRU8x7HQd$mydebug/iprperf.cRRU8xRRU8x Ň}AF&='ydebug/iprqioac.cRRU8xRRU8xSђ{5 רk debug/iprshutdown.cRRU8xRRU8x#{5Q=.(YȤ)debug/iprtest.cRRU8xRRU8xwC!5y n\debug/iprwritebuffer.cRRU8xRRU8xC2o3HV߽ڰ~оinit.d/iprdumpRRU8xRRU8x-h=Fmqo"y init.d/iprhaRRU8xRRU8xjo|K7Ve ninit.d/iprinitRRU8xRRU8x|SlEl)$rVZinit.d/iprupdateRRU9i!RRU9i!RvDg.D֛ipr.msgRRU9i!RRU9i!eI];ŠI|SB}pK iprconfig.8RRt1V1RRt1V1|0}N`ߏP iprconfig.cRRU9i!RRU9i!縆{Z̰]5y< ~ iprconfig.hRRU9i!RRU9i!F-uYt~{pR<iprdbg.cRRU9i!RRU9i!Ml# O~! iprdump.8RRt1V1RRt1V1o~Ӕ Ғk<{j{ iprdump.cRRU9i!RRU9i!Jo 6g9 #0y iprinit.8RRU9i!RRU9i!Ps ]I_N!8! iprinit.cRRt1lRRt1l[U3vs<߆~0iprlib.cRRt1lRRt1lP~ZNWk]diprlib.hRRU9i!RRU9i!oD.ivDkб iprupdate.8RRU9i!RRU9i!:LZ65& n iprupdate.cRRt1lRRt1lc}{SV^IW^ԋspec/iprutils.specRRt1lRRt1lɌB Ҍ dx% version.mkEƬ[Ⱦvzwiprutils/.git/info/0000755000000000000000000000000012275251105013223 5ustar rootrootiprutils/.git/info/exclude0000644000000000000000000000036012275251105014576 0ustar rootroot# git ls-files --others --exclude-from=.git/info/exclude # Lines that start with '#' are comments. # For a project mostly in C, the following would be a good set of # exclude patterns (uncomment them if you want to use them): # *.[oa] # *~ iprutils/.git/description0000644000000000000000000000011112275251105014527 0ustar rootrootUnnamed repository; edit this file 'description' to name the repository. iprutils/.git/config0000644000000000000000000000052412275251164013466 0ustar rootroot[core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/* url = ssh://wenxiong@git.code.sf.net/p/iprdd/iprutils [branch "master"] remote = origin merge = refs/heads/master [branch "rel-2-4-0"] remote = origin merge = refs/heads/rel-2-4-0 iprutils/.git/hooks/0000755000000000000000000000000012275251105013413 5ustar rootrootiprutils/.git/hooks/commit-msg.sample0000755000000000000000000000160012275251105016672 0ustar rootroot#!/bin/sh # # An example hook script to check the commit log message. # Called by "git commit" with one argument, the name of the file # that has the commit message. The hook should exit with non-zero # status after issuing an appropriate message if it wants to stop the # commit. The hook is allowed to edit the commit message file. # # To enable this hook, rename this file to "commit-msg". # Uncomment the below to add a Signed-off-by line to the message. # Doing this in a hook is a bad idea in general, but the prepare-commit-msg # hook is more suited to it. # # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" # This example catches duplicate Signed-off-by lines. test "" = "$(grep '^Signed-off-by: ' "$1" | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { echo >&2 Duplicate Signed-off-by lines. exit 1 } iprutils/.git/hooks/pre-applypatch.sample0000755000000000000000000000061612275251105017555 0ustar rootroot#!/bin/sh # # An example hook script to verify what is about to be committed # by applypatch from an e-mail message. # # The hook should exit with non-zero status after issuing an # appropriate message if it wants to stop the commit. # # To enable this hook, rename this file to "pre-applypatch". . git-sh-setup test -x "$GIT_DIR/hooks/pre-commit" && exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} : iprutils/.git/hooks/pre-rebase.sample0000755000000000000000000001152712275251105016654 0ustar rootroot#!/bin/sh # # Copyright (c) 2006, 2008 Junio C Hamano # # The "pre-rebase" hook is run just before "git rebase" starts doing # its job, and can prevent the command from running by exiting with # non-zero status. # # The hook is called with the following parameters: # # $1 -- the upstream the series was forked from. # $2 -- the branch being rebased (or empty when rebasing the current branch). # # This sample shows how to prevent topic branches that are already # merged to 'next' branch from getting rebased, because allowing it # would result in rebasing already published history. publish=next basebranch="$1" if test "$#" = 2 then topic="refs/heads/$2" else topic=`git symbolic-ref HEAD` || exit 0 ;# we do not interrupt rebasing detached HEAD fi case "$topic" in refs/heads/??/*) ;; *) exit 0 ;# we do not interrupt others. ;; esac # Now we are dealing with a topic branch being rebased # on top of master. Is it OK to rebase it? # Does the topic really exist? git show-ref -q "$topic" || { echo >&2 "No such branch $topic" exit 1 } # Is topic fully merged to master? not_in_master=`git rev-list --pretty=oneline ^master "$topic"` if test -z "$not_in_master" then echo >&2 "$topic is fully merged to master; better remove it." exit 1 ;# we could allow it, but there is no point. fi # Is topic ever merged to next? If so you should not be rebasing it. only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` only_next_2=`git rev-list ^master ${publish} | sort` if test "$only_next_1" = "$only_next_2" then not_in_topic=`git rev-list "^$topic" master` if test -z "$not_in_topic" then echo >&2 "$topic is already up-to-date with master" exit 1 ;# we could allow it, but there is no point. else exit 0 fi else not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` /usr/bin/perl -e ' my $topic = $ARGV[0]; my $msg = "* $topic has commits already merged to public branch:\n"; my (%not_in_next) = map { /^([0-9a-f]+) /; ($1 => 1); } split(/\n/, $ARGV[1]); for my $elem (map { /^([0-9a-f]+) (.*)$/; [$1 => $2]; } split(/\n/, $ARGV[2])) { if (!exists $not_in_next{$elem->[0]}) { if ($msg) { print STDERR $msg; undef $msg; } print STDERR " $elem->[1]\n"; } } ' "$topic" "$not_in_next" "$not_in_master" exit 1 fi exit 0 ################################################################ This sample hook safeguards topic branches that have been published from being rewound. The workflow assumed here is: * Once a topic branch forks from "master", "master" is never merged into it again (either directly or indirectly). * Once a topic branch is fully cooked and merged into "master", it is deleted. If you need to build on top of it to correct earlier mistakes, a new topic branch is created by forking at the tip of the "master". This is not strictly necessary, but it makes it easier to keep your history simple. * Whenever you need to test or publish your changes to topic branches, merge them into "next" branch. The script, being an example, hardcodes the publish branch name to be "next", but it is trivial to make it configurable via $GIT_DIR/config mechanism. With this workflow, you would want to know: (1) ... if a topic branch has ever been merged to "next". Young topic branches can have stupid mistakes you would rather clean up before publishing, and things that have not been merged into other branches can be easily rebased without affecting other people. But once it is published, you would not want to rewind it. (2) ... if a topic branch has been fully merged to "master". Then you can delete it. More importantly, you should not build on top of it -- other people may already want to change things related to the topic as patches against your "master", so if you need further changes, it is better to fork the topic (perhaps with the same name) afresh from the tip of "master". Let's look at this example: o---o---o---o---o---o---o---o---o---o "next" / / / / / a---a---b A / / / / / / / / c---c---c---c B / / / / \ / / / / b---b C \ / / / / / \ / ---o---o---o---o---o---o---o---o---o---o---o "master" A, B and C are topic branches. * A has one fix since it was merged up to "next". * B has finished. It has been fully merged up to "master" and "next", and is ready to be deleted. * C has not merged to "next" at all. We would want to allow C to be rebased, refuse A, and encourage B to be deleted. To compute (1): git rev-list ^master ^topic next git rev-list ^master next if these match, topic has not merged in next at all. To compute (2): git rev-list master..topic if this is empty, it is fully merged to "master". iprutils/.git/hooks/prepare-commit-msg.sample0000755000000000000000000000232712275251105020335 0ustar rootroot#!/bin/sh # # An example hook script to prepare the commit log message. # Called by "git commit" with the name of the file that has the # commit message, followed by the description of the commit # message's source. The hook's purpose is to edit the commit # message file. If the hook fails with a non-zero status, # the commit is aborted. # # To enable this hook, rename this file to "prepare-commit-msg". # This hook includes three examples. The first comments out the # "Conflicts:" part of a merge commit. # # The second includes the output of "git diff --name-status -r" # into the message, just before the "git status" output. It is # commented because it doesn't cope with --amend or with squashed # commits. # # The third example adds a Signed-off-by line to the message, that can # still be edited. This is rarely a good idea. case "$2,$3" in merge,) /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; # ,|template,) # /usr/bin/perl -i.bak -pe ' # print "\n" . `git diff --cached --name-status -r` # if /^#/ && $first++ == 0' "$1" ;; *) ;; esac # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" iprutils/.git/hooks/applypatch-msg.sample0000755000000000000000000000070412275251105017553 0ustar rootroot#!/bin/sh # # An example hook script to check the commit log message taken by # applypatch from an e-mail message. # # The hook should exit with non-zero status after issuing an # appropriate message if it wants to stop the commit. The hook is # allowed to edit the commit message file. # # To enable this hook, rename this file to "applypatch-msg". . git-sh-setup test -x "$GIT_DIR/hooks/commit-msg" && exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} : iprutils/.git/hooks/post-commit.sample0000755000000000000000000000024012275251105017070 0ustar rootroot#!/bin/sh # # An example hook script that is called after a successful # commit is made. # # To enable this hook, rename this file to "post-commit". : Nothing iprutils/.git/hooks/post-receive.sample0000755000000000000000000000104412275251105017225 0ustar rootroot#!/bin/sh # # An example hook script for the "post-receive" event. # # The "post-receive" script is run after receive-pack has accepted a pack # and the repository has been updated. It is passed arguments in through # stdin in the form # # For example: # aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master # # see contrib/hooks/ for a sample, or uncomment the next line and # rename the file to "post-receive". #. /usr/share/git-core/contrib/hooks/post-receive-email iprutils/.git/hooks/update.sample0000755000000000000000000000703312275251105016106 0ustar rootroot#!/bin/sh # # An example hook script to blocks unannotated tags from entering. # Called by "git receive-pack" with arguments: refname sha1-old sha1-new # # To enable this hook, rename this file to "update". # # Config # ------ # hooks.allowunannotated # This boolean sets whether unannotated tags will be allowed into the # repository. By default they won't be. # hooks.allowdeletetag # This boolean sets whether deleting tags will be allowed in the # repository. By default they won't be. # hooks.allowmodifytag # This boolean sets whether a tag may be modified after creation. By default # it won't be. # hooks.allowdeletebranch # This boolean sets whether deleting branches will be allowed in the # repository. By default they won't be. # hooks.denycreatebranch # This boolean sets whether remotely creating branches will be denied # in the repository. By default this is allowed. # # --- Command line refname="$1" oldrev="$2" newrev="$3" # --- Safety check if [ -z "$GIT_DIR" ]; then echo "Don't run this script from the command line." >&2 echo " (if you want, you could supply GIT_DIR then run" >&2 echo " $0 )" >&2 exit 1 fi if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then echo "Usage: $0 " >&2 exit 1 fi # --- Config allowunannotated=$(git config --bool hooks.allowunannotated) allowdeletebranch=$(git config --bool hooks.allowdeletebranch) denycreatebranch=$(git config --bool hooks.denycreatebranch) allowdeletetag=$(git config --bool hooks.allowdeletetag) allowmodifytag=$(git config --bool hooks.allowmodifytag) # check for no description projectdesc=$(sed -e '1q' "$GIT_DIR/description") case "$projectdesc" in "Unnamed repository"* | "") echo "*** Project description file hasn't been set" >&2 exit 1 ;; esac # --- Check types # if $newrev is 0000...0000, it's a commit to delete a ref. zero="0000000000000000000000000000000000000000" if [ "$newrev" = "$zero" ]; then newrev_type=delete else newrev_type=$(git cat-file -t $newrev) fi case "$refname","$newrev_type" in refs/tags/*,commit) # un-annotated tag short_refname=${refname##refs/tags/} if [ "$allowunannotated" != "true" ]; then echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 exit 1 fi ;; refs/tags/*,delete) # delete tag if [ "$allowdeletetag" != "true" ]; then echo "*** Deleting a tag is not allowed in this repository" >&2 exit 1 fi ;; refs/tags/*,tag) # annotated tag if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 then echo "*** Tag '$refname' already exists." >&2 echo "*** Modifying a tag is not allowed in this repository." >&2 exit 1 fi ;; refs/heads/*,commit) # branch if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then echo "*** Creating a branch is not allowed in this repository" >&2 exit 1 fi ;; refs/heads/*,delete) # delete branch if [ "$allowdeletebranch" != "true" ]; then echo "*** Deleting a branch is not allowed in this repository" >&2 exit 1 fi ;; refs/remotes/*,commit) # tracking branch ;; refs/remotes/*,delete) # delete tracking branch if [ "$allowdeletebranch" != "true" ]; then echo "*** Deleting a tracking branch is not allowed in this repository" >&2 exit 1 fi ;; *) # Anything else (is there anything else?) echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 exit 1 ;; esac # --- Finished exit 0 iprutils/.git/hooks/pre-commit.sample0000755000000000000000000000305212275251105016675 0ustar rootroot#!/bin/sh # # An example hook script to verify what is about to be committed. # Called by "git commit" with no arguments. The hook should # exit with non-zero status after issuing an appropriate message if # it wants to stop the commit. # # To enable this hook, rename this file to "pre-commit". if git rev-parse --verify HEAD >/dev/null 2>&1 then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi # If you want to allow non-ascii filenames set this variable to true. allownonascii=$(git config hooks.allownonascii) # Cross platform projects tend to avoid non-ascii filenames; prevent # them from being added to the repository. We exploit the fact that the # printable range starts at the space character and ends with tilde. if [ "$allownonascii" != "true" ] && # Note that the use of brackets around a tr range is ok here, (it's # even required, for portability to Solaris 10's /usr/bin/tr), since # the square bracket bytes happen to fall in the designated range. test "$(git diff --cached --name-only --diff-filter=A -z $against | LC_ALL=C tr -d '[ -~]\0')" then echo "Error: Attempt to add a non-ascii file name." echo echo "This can cause problems if you want to work" echo "with people on other platforms." echo echo "To be portable it is advisable to rename the file ..." echo echo "If you know what you are doing you can disable this" echo "check using:" echo echo " git config hooks.allownonascii true" echo exit 1 fi exec git diff-index --check --cached $against -- iprutils/.git/hooks/post-update.sample0000755000000000000000000000027512275251105017072 0ustar rootroot#!/bin/sh # # An example hook script to prepare a packed repository for use over # dumb transports. # # To enable this hook, rename this file to "post-update". exec git update-server-info iprutils/.git/objects/0000755000000000000000000000000012275251105013721 5ustar rootrootiprutils/.git/objects/pack/0000755000000000000000000000000012275251125014641 5ustar rootrootiprutils/.git/objects/pack/pack-37701271b2889cfdbff58971243e26e6b15b1411.pack0000444000000000000000000427575612275251125023610 0ustar rootrootPACKOxN0E|Rhi1+MtG_gj%ǿ1Qwnݜ{sy%LZVzcuz]P:[hBtT_)34.F[ 1m5mKMWeJ>;-=L)Wq",2CӺJ뤂Z)Ή1Q=F#u4m< |\@N9mx0݊?8V)SK5~ |W}tSxQA0 (PUU/+y;Kĩ7C[=m8'4U(k~jqQ)iӵ}'v3 ܔJ짡d diNLF%t%D!0\^ȻcMZnj+4ԌrJ*)En >cf3|ns^3t$i'!Ļdi!h(S P\H#زQW_=x!0! OJShVj>b\[ʉj8ԝx=N0{b.kGqE69NXni@ڂno^-P!eʢ< e($ɠ0ݾkL?>7TjkBJZZ r;Nu6s>'K3B{3CˌuaivOow9^.йwcy<xN E|6R 1.5}ǔIK޸qwrrsrs"ѩ~sINkE(:>)ef;&@vA*Tp'F33,yR ?(?tX=Rn][w z#4 sVr$SWraO%vÂVcUPŖ c3AmKټo M-FLW~ʾ&{xN1D{*}NvqB Q Dgs&9;텄 h4zDžHwڪvz'V+zڭZi$T\℅mA+ۡF:Bg\klhRHy^)[iw.pi4D~l\AVmH)j;FfBo!8mXK09/ 0,>:E_ ?h[;nM( 'l؂ĮTTA*d ʣ}\=YgѩP,a@)vь=&ݢ!R5)ay7^z;wz?'93h(uWSa(/4eYw.mӒaH+J'VΕR3HKvxON0 v6gt$*J h %8DKD5h% w}a$枻˔ 1;Ic;nV7;(% %x%tfwRZ\e>4[5jZm,?")">9D[_\/cX-̕%x4}>3yxON0+q[rcʝ& ſjbbܘsPX/0%\c@:/z<q}T2cd!'ȁ;c arw'l{`x"]v()՗xN E|h;1>Q1S fF^ܻ0qwrs-r1$,N4:!G'\p$d 3l9!Qa RZ\+jUa-1+x1lxp_7qaZr:8g-}) vL9O9 ["^"5 Xu| tڃ_-s<&izxϱN0Oq/ȉ8K`="uq$$N~}.E"0TjvQV [vM-UW6#6a@]clx5RcUFV%9cW pͅuk(^ 3PSo8g9=Sk8Ld9E5 pm"aZ20F10N)OB$[#`0KIpc1]έm mt|˾HxJ0E-4MAA_t&M"v9"8vc :"ֆu$N-J@g&C@{lOn$dXwSש*Rxx|ZOה[{dm68cjDhZj$TZֽv?Y{ jSkZJLikW9]Ħc3|Q}VvxN0 y ?VKӄJp\Y#֤Jܭ{{qqAgS@kYu^H]lT-F}^UulA&MYE%HQbUeÑk|Lޭw{؝-`ݼdG~̔o)IӶu䎖`v 3CBg愰1!1$6~D0tNS=:x p]Wh?9 9_I78u@$!y Fy3^ޡ^{cyןO2ٙxPJ0+n۴]񠞅&M0MJ;+(x Û7=^%ь{յC۲IJ9ZþQ5EDAtcSK+.ji%=Ugb&DxEo63Wus/[1v [PJ^`vK.nlz$2(B>_y0F$H֟B(f dciH5aٰ+Ҁ&z^p+` +*XfLȐV1Ä~1R0vgjލ_b›xMj0:Ż]YSB(@! V?OY2:rv0LZF˝hUoSFɶ"׵^+CC+̢Fژ [Ԟc7b_<(\8?CU˺k,3-ogxL@yжͲOс ̈́e4\hT#Ҷxu/2~ !1(xMo0 蘈,.Uvv "^|HP;4㡭e[rjJ!FգRNE' ]Uw@*+mcZFŪlV蕧G ڒ14|=oO_̼qUKyeNRČEl;QВV&9:_Nhh5SIl, +,'!2]b^!c>}^s^gEм/Obyˋ?ƘP̜V_ 4$Q\,/Ty4= TN@w}m``n5 qS@3xxj >}35C)wB:hPӤo_*]>ι5#7tTB^)d|:Y1q:Fj3ɪ3 +fpj#$36Q: o=xqn!nG,M# LNbNt.V5o5|8ЁOyB)&v)41*J 3)?/Ki-.]sZ1xn0~QOE&ͿB HxQ<&]; G8ff84#D $ϋ¬y!jUaTS sMbFKPԅxSSU)sɻE٧NL _ v8C+zm@h]%"t7z7~hny]W>+ꨉy zsdziAg EK uj^ \I|Zy3#Bj:+6ݎрh NwQxa9@F!#M>oaeA3[KZН5Yqtm!g(i.0qWؔc/Uz^؏A{C1dxgÞ"aQz`>ڵwJ?(7;axKj1D:E_`z !w&ri5sLY']zD&me$R΢ ҙZ[}:`V(2 AG(=hި{i Owr)u{[KXxl33,LB v1 V־r&?~ܨ_w35cV8c\)M-)|}msxKN0 EY7OOB 4h*uha @bdZ""NV5VuݕUVkT堻Fqʈ@WKnMY`L&B7ᶣ?~v>ŧG*Z ;(ࢁ o9gY]Â5&rv=}#Sv!.D.o'p^s?m˄JXwg-s'7*{vGs ^ل8xMj0:Ż~"+EoB'WKFun U"@#Q4xO-)ҡVlrtpa kqzt} o=<ÕvKO׍_25̇2?VB@'loCRזN#Ż@"&Nwsb)!>i4m'+,G. sZGDH`)7ʀrPN=5VMDlPh;Rvx >)q믙ZcYAQjeq95F`iۏ.o[p`ZLB ?c1=2L+1v[ ؝xN0DĎ㦪E@H vvXM(v ffv6DE]Ie (L.Ւт z$ Dڊ)WPQYU9\3=6J\X?锽{J3}ք x)D]eY]J qJ+ B! R^'(w.YzE_ !7V,A?5=A\S=TTGn05Q)V t0N0Bق@QLv\`'[mybN3Ӝ c_n?ii nm}^?͠Tױ`q+;ť9Y ^+} 16 %? B \Z^%ah45~k"Nr6xMo0 <@cؑ0lb!T,T<}~('0`×S@DħG-j5k5oxF7|8%0rànkEq/ZQi[0I*c}JFϿ$|~R5.U2\)?;QpռEg?"3Kڼc4EXcj##mLBK3cOtv*@M^zCP|w:0g6@9.>c YhLJh2r`"#॰QWY T>zks čK3tFFJشmE.!9L#48,'uDKF|>y9r}:s).0%);M~0v-pNFq9#sUVut6^U}@&:A_xN;n 9+–!j R$i îqt"M1ͯd"P$AHtNILF9X;; ɞ% 4:5q8|\) iqȰ%f_>w {P^+^GȡTױC]}) S_dZ3h |8`ipI8xzf=ǔB į}fu#Hn> }9ms~}o̐xO[n {[QU؍b RfwvfvZAi)YiQQOjOL r+@FuV## \Ĝnc:v [Z svnÂŵl-‚u;J\*ix&,їs@ƾf6?+s~E 9 v=D{\_FxKN0D>E_ O~B;?ģĎlܞ aĦUz. !PUZ٦Ս"5T1kdS C#to(m$Aw]kA8L $jk|`OOW ],>G^34R2.;K /h~K{Kʌ-VXQdyG8=xK&uD{Ɵs¦pbP#Xn-{_]}S@[E*}وYxKR0 >.ЌIa,K&qQװ"Cm[k;:;fSIU{[/t)i9@LWB^?%YcF9`]a{F 'Ht3;i6\ C^YWT)tU&rv#y3|@mI}ש}GY5TSսNǶnČl-ʪnPۦĶ"uU}DeJc5)=z,pYR~%)ݦ73/&oٜLcӔw%s0@0),>Z卼C^~qe9EƄۺ\o.rErE}ô ɯ>_/X 8:?[fUxMO0 3иpF4NꮙJ=8@zlQC=SFv؍ZT r u]EPcՖSu-;mQ <rf;]XPdRW."*ZB&[)E-'8}U|qK'2v^wh7y=Sf4E_`"_{%J\5z\N)t-lW8os_ZRs+/ԠQCp'!^i/G1 xn0 w=n! {(Ȓ!Mڧ^t?ށƢlR 4%4mJJ,(0{[]_Il˲jNR-ASLIS##sKpn7z->q\8BYM6SbSgLwQ%|>ஐfLD2I6e`B fLFqL] b@ mSD~7.h8Rmb3 R$H*81a_#$(xͮ0 y oݩt^!!y&N{Ӥ$iyzYxJ0D4M=DA7776)imŝ fS 3H+C ( ZZRh58Z{`Thb"ku!Q$F'-Z1x1;<|pO4Ŵ{D77GJ[sWpiMۊ#c_DgAiA;,֐ S&>oB(g*oʏ')~x> $j, Љ^v%F/O(i&XZҼNܩnGR.= PTPic/9ct4u%n9?7/x xN0 y @KI;!81W'qhmR)ޞvNl?)* *(.ZԵQy)6b$QEI%R[D)40dqyKX)᜺wB*kL֍VFNbeVB+rZVnv+le#^['tkuke+mfCIaAܼg\K'um-D-D#1cu6eh,!_ {)ocASZ`ꯉ`Fq1K0݃(^߂3EOOpɬGȊ&xGG0?0DB|[hw,5)6=8Zӌ@Gv[._CxS]k0}ׯ-=;h7ְ1,_%dHrRڮ0XHs9rVs\>k$ixK=6nVU ,BEf<x Xn!dvQ)R 㙔zPR (, XB\0GVLLb: ^i$n[[zz5閼 l~NuAqd Je5eK?k#Ftx^wOtRkE(Z(usq < 1xM0sܕhIڬU @JH\=n-G_ed2ϼ)XwB雎Æ(Ǝ^K];a qTCs -iPc+ԺQC/!aNGPSv>?![k\~ΎReƩ~z\][o`UfdR"vi'Sx<*)w'܂.λqe2d˳FImL֒ˑBȎc-h4ׁ#,uiyrtsI)»/ gb9fZS[-E3ܾc I_D a70^8b^"(vn:TD?p[rgJxО !~(Ҙm3,gS98R+j]a?n xNA @# Ziн0H ȤM ,ٲl܈`HНɅN<zVa2MfTYOQ)Rj=Q9;k+͵r~ BR]:[NǗyYBjnEnc3ժCOQ3ftMOw z@]->&{ژŝa ,oT<@|; _@A6'Mo;8ߚ7훩x}MO0 qjI~ӄ8MH CpuH)M'@ɖ__;x"UYk}%y۞aeQXГ mRyHndPB)k&yx' 6'kh]12Op7E@8cIN:lC $I/Dנ9R /`0~s$A FI qj"¤ejC1y!#%~ o/=8{DQN Ɲ,){n bPxU10 PUl~;l,\$#BUйVqui-RuTc񌮜Qd-*6E%kZ&On t(>_rB8ߟ@/_&5xOo0(6VRE<Ǎ^9ΖO8 -(7o%x"eEU5ZتUcyUwo=2eVn(:Ҙɦ-TS)K蝇O#uҌ.›a=ߍ.ӋnJB^IVf௓ x'Q_@AhHOx<\ͭ =[w@ysYo=Y#xy]`edagr%G$Zp-.DLJc.CQfօd*f dcv& <zL?`0U &v#E[/[u@L6p c.ݽdwE)s o~%Ty1%N뤻>&~͏&2xRMk0W3PJKYW,I~vc :άR@NH($e7⺔mUĺe#xn,xnڼj꼓UQWe]s( !E{a9grIb.RS#\K‡qsvu2MGȅ(s'.8g:h+^5/aI{._.0Dh&9 \xt>`ɸ҈A=^»%6J: v3MB8tE.&l<~%5x7weq '~z8 pWTin,.J!gHRa)U2GaI{x4LkoNL+eP[qwkl:h'ǰ%.x] &;(NQqFKKlc?]Mӄ@9ipVx- uVgІW [!VZ6򠍕xjL<73j+s姂W?G|z{ RVVK..g#5ԁuaq&XAe\"&>yHaAuc*  }(;SR#v}XKJr~4> 7{浸Tm:#ui& d,72\Su~z.w%LT453JR,2 ÆH󌔞l=^l&K!wfoY$ S8쨑Dqkw'g`xPAN0DNm'5TqAĎ%gjfgF[͹&)QjEZkҶh*Sr+ITX!q" Im*4RZU6R奩J#. | J`z痟) ="Vۍ YbcdҪѺ>!%~lt۹1EL,z0bE8C_%=緗=o(4l_9;F~0NɁ\3kBmq}yjжYnda%xOO0 q6]B B+n]2gP>?@.VN$AmWn]uFֶ'ؠ+LcZMe#yzVMk#Siuu5\a1Dx܄"\/>.dni Vu9}۵-,TQw,R6hx ",&7˧S*Q>f)]WwsDd &IțHI`!VRp)8ɟ %"2;xtSUՇoV ZrfD ֏=.&4.03EH 1=7b_X bi j(ƭq0̔6鍟f1xN0 y ?ҦMiB q1$:FKIa vC_l8r-W_WuHo$% QS g JejVUP.k6ة% a7n?zp/#VE{(Eu#mJ4ó#{BJ4>,_en)2;K.n`g #X/;.M#\xA;!ZYB}o+DSset1!DxC 4_q%4az%K(x9D)aunGeh )ǰ~)8|V/="9eϴfqː~E)AY[XrP\WTiQ1tZvM]5Ep_|;;_wkzΎ 9eOoi*Jsn'#-aIGeу&;ܞƒyYa :~|yB?hc#Q`֤!Y  Z S@7w d Q 'fHo0leS2xn @| Q(9lWCM "dc&TiزgLڈDpV G3-?Z^3'$8vP+ozeI*G=ý /5xO8_kYZ(yDױ]B)y&Sӄ6̩`JrmH,y/aގ0rC,1Z_UoBtV'`Α\o;ʳoN9xN0E{@qZ!*x"#?chfFsFG9"NTL^sj j+e[Fey/ɮ" zR-C>(!G+nD5R q!&hD| ^Û :_Vu[k SJus9cu2n 9XPQ3zHKvk:;@.pRI;!b*V08ǥ 6N&~>x}PN1 +]#*g=5q6b,-_OZ [h`;bk'9){-YFnp$V3%TjmGS;Rc6vmPֳP! yxu`I|(rӧ ,籇Mյ(ۓc }_ vo7((z(3%mH3r@y6{b/-3 ,c-N%JB\a/1HQkIQ wOfgu4xN0 y @Kg2M;!. qmRxz!\_ɶ~"QDsjPmKXd AWZ5ʚhJDRFhƢ%> 9xu}'w<.8ʏPT)D-2 Tma Li/|4/oMt),z:LSK8J"9مtb칷3L2IHO_9cVkNٜnZ@?EmHgޘ O'{ xOO0ς@Y1`u-)e^җ@UT-Uy)j͉9 MgE&de&s.J\PLוTSbYl*K蝇Wf`w${".5rLo!iJH2e,NGyxHo .`~#23%a}#PΪůF46E^!pU sof0j׀쾭j0Ώ08!rx{:H69cKx݁VV)c{Y҉kDa'_xKN0@>\Χ `.`;LZ9m9=l5{IK2M#ek*MiPP.K1`d )UT.;eciKlWZ8}\;؜؟]t.޳pXpURKXJJGGxc!İ 4^2 7)~\ã/Ny0x SnBwqrz(=̥g+_9;s}?Ƅi/i!<"t\`mO% xE̱ @ @EiSd~ T,";%BQf:bfL UeKeB$ \r>?5Pf*̠d% 0Zm%ie%2k`^G`nM({Z)H)J@0$/4=Ga3a:01E^ѭXLB{?Œ4A$rP~dSv"S$)_ (Kp#Waq׃o*\z.^JSDr,g-T˛xPMk0 WiRuzr,7fn\le]S KCq$cfQg[YզhۍRTZUʠ42dVl)lZviRZi[$p!D8<"K_{$.>}8@TJ6%KY"Oώ9=\;Cn;s'v> zcdȶ74 ":xb8D z<=w tI{Yt.3G_\K&xN0 y zaU"&$(/$hq7ӓ889gDRbSiUl[_5"5%NJ6[aI8Q` lVlKj.:kU{ԭ86Nؓ :4,AOo)>.^-TjU+ R3% ?N3>]`}zON$F[:!:XC`p412Lm+hq<"sCabvT\=|"/ mq|Fw>×"S2GQFy[J 8F`F3tk|RDw5p*!6~.s5xPN09N3-g]bG}=6-Wvv硡 J~{-ǣBzĨGm#[1H #C?\ye&T\H'OF%.-yq>.3iy~b\p*J{&̉(upN t=YXFa.V9,w@k5rmЂ6++cofWZ! /@R$BsqjN5Jf˴R;!y㽵W)xn <]6Rg챣(jM:}A5b>}$۬p9Ā=M]ZmWw{jN&E=(Ys؋mn\K%ZT5)^|_ruO~K٨ϣqzv+l{LpjojQN)ɧĘeGTy9ތ>FѧoWBf@%;A%['s1a ;bKR9ȩWo1$>3f 8$ ܌z9 eӟa=bc|fOESHc YJkY u^<^sO2k :?KE^oS8ƒ P&9;v^Z>?xSxN0 y 4%&BB k6OO6N8Yocj4SF[;m49^5jm5e1!֑iڒFQkuIZ:`+p}O.x繱W8Wގ ԍRVJJQϹȞ!qF/?Gy~H@"H49CWR><`3'!z`3ȇeS_wG<NTx̰$> J?U캕=m'xKN09E_m{sĎqzœ] cΥRb, ji1:J ԴAϔv2{s&e)#PM{Rڙ;d[?}=v~ϏF<׸ͩo PVh^P!N׺Ufǩp90 W_{J[?f V M-eDxSn0+a[dAtZ9SŎYzhB--PD\u(E7ߴ͢j]UUQ+% 5JF9,jXkqPEYRvF*)7,{7c~im`)t;̤¢X|( ௃aZmQjbt.Dٟ%_X0УOQp ~y Z!bLde΢q3ŞTsj+2=Cr,=?jcY8-5؞B/ q SF7 <tq>]⾳@ű'8X:yeHz2Г=-Gn%qOǻ?_-d8"OBwYD^^9-dK9@L(dZd4㈸`B%{F&jN8ƭYRW^ |>xSn0}W3mWeC*ZJTc{HU6z]TDHb3ĞJ,WuA6XJU2Se\l'جΉrFʥ,* *˺8&'pOu~|Z<ŅSB=dEV2+$\H>v.FNܒkC :W?goĬdAX0p~.yjL`Y=m[xpt亱?vIU,g4 1nK \4RCݺ*Ol?=F=.7^UHBo/gnvc ܆ {6\ BR1B}@y)'ӡ7I6 (xE2dO+Z;欎K2jiz9 k`텚.VCW!xMn Fbv]ٲM쨪2 5 %zK|492C[e ii徣׾x `dA'Z[)y7vG]qy5!Ě#Ѹ)aFx;o19\;=&ЪVۮQ F5(tv9[ݔ` ȥe{e,l)1n '!N#ãz4kɿ BR1|̘]poçO,1\1KX f3ŒgNex3zаΏKiZLU$~ش&xKN0D>E_ Q3Ag9sN3H`;3cOlX ]OU=3t-h )5TURVuSju*ZTbA6vpm5DlYw$%@z,5={8"@3pK~2v}/cn9'7߁TRR5PE!u61Yn.8&m9zC@,idH -6dH!dKjxZo^ 58;mpق aM-쨏d9F`HH  qEO=j&'{YSW\9[֙n8x<ԼxKN0D9E_!,599WTEpu>ȅS&JyI碓5fRp(1.G̺*KpD=WL&㍕>'%LFp nCFΏ ոwJpk^|ڽ=.v =ߨ]˔ ^$hp7Op+Vb`%*=\|_@#J;'&0N%@ B'N ohH09mT f>0ȣ [yr)2jtŅvA8C ~}z'F?a}W%ѝ]vP@xAn b> cpE/U7VRZ3ĨZ:Eǚ0hmNn!cԁ%rf1q~Axެ k nR""í:?r*{x!>8io놃=itO"*K+#0ȝqS-Vێ=aƙd g!xN0 y $mMqqH]#ڴjq$E>;I 8m$tgMJRUmMHpk.+l iDi"&:g1RQiҠk9+-?> >Kq cN=R6]a#wR Ύ>%n{:SJ8Ô->KWE]*YdE%'?;b3B1t-=W/=A@䀧ˑ qS^m&o4{ q9̬ 3 'YrY!uy'xMn Fb.Pğ (3t=;$Lѥs]$ /sR$U0b*B^YjpR\au^)RkXژ$k?/u|WbY&}FxSV)q[a{ezS`Ͼ ַ|iN+/?fxm @;UL0`(=ǭAA1RCKtf O19BkIDbëQzѡL^3t>()JkS6[ Rs|;Gp mpk/]{9}9x,%%JKW/RK)ZGQ>GٯW(G. 4 Wy?^XxLIN0"'b4B+9p y0xxRIZ!ʮ>.Rsf^hm\YBCUTD2q$.BiBA 2Qxgge8/ VwK!FH[{/qe 攷 -/=ŹF 7pκL }Kkl?ǏhNtҹn-tZK >уE%.vJ넵>U ,bxu=N0{b/@ zBJWPf7`Q SfFi&$th&6e$Qw$vz?0u#7IYw'38[zu<&(yWeׯ9|&)MѺ-頵KY8R25jh*ҕv800rT>[XM*|)v\axAj0E:\F{, J)D#͘b(rܾn{edX3krBv C:= նOMT2] gE.u y"8gٷm}6 QOxK / hAF,~S)g֧,p{{A:7IO tIQw8뷄)ֽy{3"!lxz}R |y[[Sx1N0Db/@l}!w֏vs$4ͼi45J0N? 3Z(-1wϲV@{,iˣ-Q"5 M'Q? EHry^z|>RyKp{3kAMJu-MVUu;~o{>j\# ʱ[0E/gJRsdRJ]x]N0}SU2nkN?+12Vrg#Qb8Ւz oė_W1{;z3 #savP3\0/te_2W0dRxKN!Em TcӞ|EzxG^az]ʛ08–\VJ<*d$,bj8c1L(hp$ABgI9eb ~ɢM3Lkܷ}up)xN0EY"BH,YIϞ&J0|d`_>zMK9U]i(Rl]Rf=<0 :n\6,SDS+LB|UDD 6v1 Yt>' ?5+-w J!w dhm18x B&xAr EBhv1NRYf= bU0w9}h'L͆*T8ݵiv(TOjP)e:Ӛ)X1Q(p&9)p'cap?cVi8ZQV'M .hR"XK"<9l5PiX/3$$p)״~X9vš=޹Y= L`f a:YszZnG*J`eS-RR,N ^=jg$i?gA\(@rƴDʖHǂbN^@℄Q,zKtBe*i_f^-̙e^wS՜/|^oq.(dR(y:W^kŜoX'3o*vPgx=N0>p=GT{Ռ=f#8rоޘ+:8KtйLYZY4X `a㵋DN,Y)##0'L*[{d]@4ёI<623qWL\{"o?z|+qeLuy B^*PAÙ.S5\]\o3~hoELmxнN0 =O*M9!x8Ժ\S) !&h˟J&jHiRz- {~+bơYV8|, { ˜29of^@ֻ^6$l*UUl'LQnT[(V!_aN 9^fB .7apw$c &  \)8V cLfLb|+"FN|gnxKj0D:E_Fe[}&hY}G|N;M6U*FA kRSndMV(ħEb7cZt'j.fT07x dhr),ku{ >շvS>sKsd~jvb!(WoxiP &;5ƕd6/5O;.0ٖ_&;טO x]J0߳$M""tѷ 99`~J\ufDhvvF!%/rV8qA$rMNhSZi& z3^"Ya5b#XK2cO12>cIpNìsv)J¶qk"n |XC&zg 3L+w~e i&xPKN0sqSS!* {X$vd;*y=/Rֲ)jgB%y e#z"k]r#J%y% ^ʅd8yx% Jӻ(M7U덝ޖ'Kqia)p |]W"˳%t01923)>l3nޮ|7xo/+6Kr ^NYhx9%BjT -Y9{xߟ4 1Wנ1`Kp9,.:O\ly&R/[igJIw@/Saz*#2y<`Kz,4fʕ3q]O7a'MዜxAN0E>\]'N]!Ċ p{`O<Z"ߢsxvᩞᕯSA@*7zU} k!*H6^.˃>oP:v{ rxMj0F>\F_ =Q[ 8N {|i%wQfω'll)WR@ CX"H:|Ù6? o_Ou{_Kݿ{Kƹm')b馑F]V `xN0 y !I&\9s84ZTQ$N6UD0b^)ТǻF^s*(Rl+f{D= mC`QB9q̮4 ove w׏Ν/ ss"j˄i*D±Ze34וҴ)Xy. -i$T1-0[# !DNg;aWbܹmg/.xn0 E +]vUL.DB=Gח֤H7eRDeC*-btv V}UwJ=J5P#UՉd_c7 H](T)lھ<||vyx?RExR BZW#hvoO_`V <̄KBwFO1yK -ɄD<Ër~ ^.=nv} ka \::YpY2:6ocǢxL2*#DS6DpxІI/m6lX$ڡ>dt{^'@bi\huo;NtxΫQxa xN0 y @il&8pG45U₄?5;a6MVhéZj6Ad#N4dPzBθFFK'5I]k2s'xe x M;_{ׇa2SxmTJW[)伮=dr61Om4ZIiVX,ӜCrG HiHȑc ?5~'z=>߃DpJ8+]} ,dxN0 E TIig4B DILi*q)t$V{A6Mg;)0Bw\؆жᵮFlV=7K$G+xʺXuhwjQkB7yazOY|x)p-,W'Gm/#.wc `.aRp'-}m&Vrc:1y-=d(B{\YQ?) Kiwfq@^mDe9&up˃wB{SF悱z4`Ao}xN0 <_& cf'q"ҤJS@HL$L\K"B)o5-)D @H3ںqTj[[ GS}ЏּG*sc\(ڡ5}){ kJI` }"߾WA]w7jd~[a=B=[|S%'hKNv3dk _th@ś_alH;xxuN0<ž"Np-x-,8J61T|  cJz٢O!H;J/ vC]Q'ő؆y/ѹ0\>Mxr|an2_Aw+5XoI9Y^'U4橈 r|\>׮ƸJ+yc]&P19C9"RSgYHx̻J1~Z8$ DBA ˒+?|P,:i_2xAJ09\N3D\q L0&Ҧ =]24:Ĝsf$.\LXR2a!E!Mq}ZWvQń^WbY[ ­_y-UPdb۞`Bf<[]a#?y)wg 5lz~J\axMn = bIUU7] TTl"(U~gtfH!.[O&2j Rgޥ8$d5vHBvΊZ)3k)&κqp=))8 *jIwS@޽!N_jxʴiiirQrQJIbAYAb̆ffٶhxʑ;\(-kAf|>֥򘗸ͩm ;Vh B 1t[+N/bpxkue`G}L?{iQx;n0D{b;W F*`IB$*飤  !8J(BhMYTۘK58* RK`%6%aTdqJTʔbCڛc hmkpY8q+yzߠ0\_r紎y[AqrSq8u8r{cHjg8x?\iGibxKj0D>v }j[d9 9}L9AU(b'4ho#G0'rac՘(hĒр1`&dM̈' {-d0( g%49-66 ISe^{_7/^RJ?Zy]^!b آ#]J]qXÚ괕͏'Ua3jexMN09uEWB=b ?38TH܀hz("b5Gx* R"*#6lăsRaBĔv.%XXMYƑV!'sFH.J qe)yWʠSݿ#=^~2oxЏT'Rxbt{Ww3d/^R4o[~mxuN0{?ž:t:WP퍰H(qcF33q GrJK9i-ɲ260;ԋ6^*hVD2adZYd--q( \n .&ͳg(vɐ]}ʑ)յt ? ޿_ֶL2ոMo`4jv"r$ rexVHW;_wb7xAN0y|N-! Zk9@7pVW4f l<32v"Sr<"!E.„STc)Nm4Sk\RE*DK=?w1Hu{ŋnNJ nY8<=}zy\I pkW+J̇z ҩ`xQj E5Q#ҡ3RωT`Lf =pOD -J"̀ROr&ș#CJ*]`glQ8$;6d8)+#"R= gt1 xsߏi}MqnB6Bs&xbcNsl{q_XB-f!<.fL`q bxOKN0Dv>Xa‰+T G30!#{MꤑW'#&,dp 7GVg'SBG62E3Wֹ ar@6me,qgГQj8Ե5xe  *gv BY\Xl_ Sg̖xAj0E:\F˚J1C ov>3asZ0Y}F,8pFrn]0-D|^96)VcIURӢ]A'`[ӄx ^w:F{.Yd93  %e.z1 34l$pe\ C- *TہҎd;xMj0>Ż@~"KR2OOmE"O3t7P]Qi)9S)$vى%}%ɳ Y8@,GE[L|kEF3K] nwW8]jޔ탌ɚHoi==V ZNu_Zx^*L|7bؙxUn y "!]wag͆@DGwſ'fLBQ{N^Ild݆S'$?K"VR 6&:ƍڻh|U#v!ޠ;> c݂{ nYx1n0 Ew{@dK.:u Il!XtW{r"$y#SoQF*N1QtZF+C4XD{:RRJky\gxkޡ=w=i-ZM˒_2u$@n[bXKF:G2+4BfSrKx& # d5;>sp i}X%Cj~gq$xj0z~W { ֫VҼ}>f3l$vl؟iZ sDƫ+ub) $\ t GQtspC:| g)>kCި';^.pmu{βt)7ϕg`adm^BuTLM xK!n#N F%Wd}bH1x9Z"[t&CKm<<9)#{1q r8^i;k GC2 ɭPPUlAx=nC!{N}~xHQ$r]b$0)QL37pʮxNYRP*6Mnyʾ [&8c%7*}Aޜ&2&< b@1hxчZT)w-{.W (ӥwr)tͭj]cmc7#M|x; 0{b@Dz-CT)|v76dǐ 5SJfHX84uHȳע$js<+MN<@Ѷ6ko½ Kw\>u1NJ46u]r94n_ibD՜xA0z7d,9yHkKH$uҗt56 FJ yٻu1fHrU!, AI ُG#%DJϑ oz+ IpѾh1,ˬ*T&yR'l5J^3LS[)#ׁz3MxA!EaҔBUj)1ՙy_F) 9%M\DㄨZZ(hqڠTMbBuʵMUv~67E~7fnopc]>P_y@Z fi:]kToKxKj@ } sIC6mǐ m 4WsjќR'}baew݀&JHPy*Pa^drxٻ~ˀ?~A1s9۶?RZ8@s{ yoXY־jǟG'xK!D"*a#MSZвP}=̀d ֨dc)]`ͦd͉;8dK%KR+Tң\ks>Z^z5yWZOp W@x\rvèX 3^븉7AMxK 1D9EI&N@i ;MEG)&b%BJHB.&ObQ6hR'VYUAtRvd$ZRNƏ>^\~}=w4NpGDܘSq^ۺP9? 궤/г~Ex0 |wiX v6Ӆk2 IEidEVϸIi`u$Έ0PJxBN}9SՖAۥ̏\ A5I([?wu]b8wX3m*Y?Q#;@|S_PH xI0~Hx3I"eA?p/U*u_و"ASS8٘̽uڼA+&=Gj>*הhBN?X>DbPιa[5yw>v%h:U x; @wN^)c7촑zf7 ֩& Z&hb?yCn٣EJ2WPSdM~~\6Zt̐\ܻ.Zk~pC6_F9x=0 @=ΒM$=Pu=]y7\%in*QI:~޽]C$\ubyp* 8x0 {MS2e pkP"cCY \sTA%,>FNj@Ǩ$'4U6nCV<šHh:: x10 ;RƎ%3ā E!J|[n *Ls\Uy PP=mmEXDb6Jh $.clw|8& 'ʾGn˾AD ^[7_xK0C9$b=)() q|Rqe=ٽ1 q63y4[Z؎8SƵFfGO"ٜ"ڒ21PR׭nR/p I)=vKy4ҁ^žYP=VR? ֺX} HbxAn0 ^{.d2P}%RZut/{Y̎nǨR %JALѴt<{L4/B)sd5IVƹ YFwCϽnw.& |>c꾛0[g-ThJ>؇/ GE xQ 0Dsd7mxf)1/`x0J0uskn子0Aُ5sJ+BO x[ 0sJylD(Ivco 80ό ()sihET4d37GZeAK1WV%b ~/I:lȳ6e>1mm c5whG_I]z;d<xAn E9$ҨR7kDiۗ7_r-oP;vq5TPNNQ݆S2z$BXK%<-q>=!?}&k/M7V{'A >* 0J [$옉ai}/-X:m|Ö]N:r ^{< sOyNL@G(3ZTlxOKnC! s ]TUQll>@DK*X#73`;"v:Z`Ӯrd0ѐs(: SzexS0"d壁lJdAa"`H܆]I~OuNXVԎ/P`Vs9giU>gyݽ>~ᵤU= hX__G[ xK 19E!I7 w^hUoSPUfP$Q=YSFjbN.),<5x6.j .k7(}DAv+z/v?A)oF[DDZ8Ky0 !gP{yM,ډ7NB$ x=0 @=IH؉i+oyv3U4'J!YyU ʏȑ*6mi@!3FD8$V,=fZ{I:9۸sZ~D|\'WyZ?}&mI;(, =xAB!&`bL<'h+?~4^`ޢjSysPbtF,/Gy8ef#2NC넬 /%xb!E$kۀ<~J3 =;meNGj."PVrgif)_^ /z@y|ѧۛ?J(xK 0C> şclSbiߔ^BuJbpd&m9i4!ޡAɜة4`Bd1ha 9[_>;aVO .mYG=j( e||T: ѻD`4vBP[ LR"g;ҕ}SxKnB1 EYT/ n%>!dB_蝜9KE#5)N"b}b72%s*cA Xd;\\ERع&>Thz7<bB1o>s%P͗:*>/h@%ʯ~M3y:|hELxA!b^]HOY%.`ؗJ*u9D sF"Yn#LdIC6$!'hV|@v>s#)z{ƣ1{एk-s2VviTPK=w>L+ךCx10 E;&u#pD  7Tb$DhdIB3;QCܤi0Fς.D&r11.i|b F\veÏGioq`6Ԝr5V]k{ Hp˦n>DCxj0DdY%K|V"u}]Be W"[5 j&*iUNzdw2 GcDXFM2" x.^S &BYLxN rKvi>X(O<@/x̞T !e~ߺ>S\lˠҶJrhH x1 w^K`@*)$j%o~ 7`,"%Oi ]d&Ύ%u, fE9!v,pZp.T!{>YJ@Wd xA0 9l։S !=Ac; 8>o3V`TIL<( 1f${9I\mk@0bP?WfQmlBv'Lp9)뺴f?n:O0]&XEhym2+m >• x1!E{N110el$^Wת #b!%&Ԇ̄s@ xqLtĞiq}M,ls4AXzg>;*e[?V@Dd\>p}䭾=xAj!E-[M AKHlki o|9 fI١ &,:(% pʄ)*D)֠8/kx&BB,m?RV;<"K|Pg U=7_NZ( ʵF.+ /ϕ$x@z})Ui x10 ;K\\7R ہh$>-tGQ&OM>fh@TP8-5[:g j#1M4L2 Gi*B]mm۽.W8|DH|ͪ@}{>jCu]^P|qڢ@ xK @b݌fJg FI6oxOeM}$\f2DѦw{T;WY`L%[f:,}DDR|i\??Ak;2"BmYJkG3L[|P_X;N xA0 y\MR !!h_ E">^2;k*S40V}!B5){E'%ɂ"qVyu1+.2/Pu:;MQ$ qx\.y:+}/5z6}E@ xK 0@9쥐4I@\zI:AHǷ|yO3s䈽M8k}`d9ZSsiEwNlYj XlK^ U!> gW|c|ԨCU5#Uﵭfj$m~? xI 0 ~%(=,dqo\M rA$e7`tuv#JZgKddt l$cFi5eΆp_[umP-K]Xճ~Cc8vǼw[{ٚDLuC97{CG8ٙxAn Ebv]T`#E]fC3ǢD7P`+rpX3'~N6N"* #uD7xW@5:F3u?V%0>|/ og3苪 7Y)p{z}|{L S)`ҊlRܥPay x;! {NAlٓ,IP!"Y)k]T( s,.* %fH΁sm؈l=Kд z@,)={?voֹ  3˾m C5Ӿ95DxAj!ὧ}QKB Yd3TkMd[fy-D jŻQLbp!q:gTrkDh9e!L"ٙS,wݿ0D'}NmkK۸C_7)è|\^?v"^n2z$H% >1?Jў x 0 ~d;e(ΐ DnCIR\g@sp\ofRFXY S uJFB!4ۺGAC He>p(Ͻyme{7@ Nܴһ>=K1R6 xK! @{7|@щΌA/ۼ͡ aFf)Fx 01g͊b=BWXb z#yq[ >sd.s9?Lw79fxNj!^RuU-^zɒM9 Ì Zzbc&1)0WimՊV$TFԤGk9nA6=r㟟@)3Y "wXs^TLG2` }[+|`oZí>wSw8NuT[[#;XƖx=n!D{Nu),9@Rɥ+ kdl_?ȹ@ &IHj5 %W@rA+RUMHpd}4Ƈ( V[Z*zLCB(y A;>BY;ӲF?4y.5soQ?1^Wt:)7*)JQYxM 092tR{gbՖ6R/|߫8NBE˭6I␝?P }UАfG&r$Q[-\r^gz++~{^7آ|ZT] ~r{oR:TzUB4xAn E9F@B'0ؙXM ҙ}7kfO դ&4YM! sd<.̱֚i$3lz?p㈮gfvx5e[h=jxSM]H.?3pW%V"wXگ3gÖʙz[%pKFUh`L.ea([< xM FNb`DiĞ6^o󒗷Fg삍3H9Y h4*_J$IlyRYS2jʄ"Otv!H&#%-O9 y2g]M*e# 1G*fZLks;[|>x; D{N}~f1Xb+ d'H*31QJFa6.RU &e *o N#8RĘum*B} ?>@)3&ψXunPsk9:חL+D[Zi+rvi H~EߖxQ 0Ds$kv "xfTbz{+zgcjQw#d@?Sj1;kV"ơ '|%05Z`(G8zj{8=FeI>4'./I!MGH"kisʿռF xI!@=)1&( hA:k눅$ԨE#'̡Jv1bwbu)4LEǘz 3)NmYǪhijWe=' xK @ѹp^ħB^TJ>[z'gr{S!)){"@d)礉$[ L$F=u`1q:oy'(4Lu?%_b*(y kz5^?4^* K =U(>jfxΜ mXfhs Y xK 19EELqGttHȀWMm ..1"VP"HY*Ncl ])cp}H9%TLd%!np5.%T^eCPymyKې[ Õz@ x91 =,Y043SEք-^XqPn6:}0uk]0I.X)ƣBs )y>ƺ+X{&Q^1ؕsOQ|aK=4x͊ >(}^iA oC79p7*g|°H $5I$mzvlT,ȣN)-L~Q+'u{XkbV1a>y ʮ#ߐjIK-{Z'ܶ sw8)Q}Z:{h4X xʽ 0@S\O` C`8kXݡL%ٍE^S4^2UH͞&-#WR'&Dk}ܟ+W@dpsn}+`} 8 x10 ;Ӧ$"hZ |@|[n }1q(zB14KTq;wkdR^ a4KؓI6E?u VN?9%QHpNuc{׭z-ý6R_poW>њxA Eb&%1&eZf*C1xm[ޘAxLQ (u 44ѲQOj\:AOF'È2Da+&I"DL,p ISsݶ;ls-VT3Rn [m uW|GxAj!D!9@3LY6ޢd0tk2&i5W 3+,M $K=-ӖH6LOa;K o_m`u,^x3*"U}~3 d.x{7Pvdg(u?L`HZzdP x1 1@>&LAf0Ltݕ+xߛ*uq r2k**a*M\%0zPjAIFR88*~ Ǽ`BJ>XoFe{?Vs=MgLG|6 xKB! @9`R>q/'1k3sX)d KH%-VW9y>m"UrFsTT] 'DU5c2}_^7Smshe4t6xn D{pkR,P΀8NIi7"@^y" 2xnvlFΪniܥ @/MQ54kubd%ZǢ,zV ?ǭv3~P3!@pg{cyC-GNpNqk rpEkG@ʍKs^}7`Y!xN[ 0 )y4ӅN/.0!I7f43Lѡh)c'ϜXX: &41 <σDƐszQ`i"\z T^z?-UeO+ "<}HgKz6F+ xK0 9ElTBl8;PUpOmf3="CD -b521S HJ4͑J-|?P x10 E";Kq#!N6TiSoxu?adJFԁ5(ΨSTN* v#D5)"QMA#&!i!LJ=Z_yD \u1?RwZ=K3iܪc>> x90{b{]ߖq`A8f9AfFGEIg,YBch:>H.TJ":OzB']BDXh}7 2&$cS ~Pe߶6'vwi+|+<[g8'W>. xM =}7 $MoC 86{zJO8`@9t2Q݈l Cj&Au:ۜҐNQMP| O/c#7zgϩ.K]֎^.uTcˮk`?xK!Dn_1(ݣD&K"@9qܡ>/F xA Ebn)2L%Zjoo?B -,~Nj68!J4,QmBWfh$%~![m+shcN1G{Ӻŭ6oy`>٘ xK!nhobhLcoS)s,ְJO()t*#ipOFit>v)d,%VR1}@VxepDV?Rq#V + ҬA xA b^K)$ƿn%Z ?\23`bHJ(9 d5D9r6 /N0xu-gd 4{bQM>}@Q ןoN!F;!\SmcG/{ӺʍYyp2o?ߑ xK @bn .S*xWo^"`RLޗupI|\e옍Z+ Ɗb1Ղ:e'̉t%y, o`(:Oo[SPU;F{m+|+ G)/;>֑ xK0 9l[B%D@Zqf3oXcfԚRbhcƥHh%V{'ESμ,EB`s>"* H o`HlESy{ڜG>k[uc/q[}[q=y xA Ebn %1&Z 7//ya2bIR>#+yV{qLP%3Gz=fax'! p4$TxG?q2di&>R۶2F#Uw8ږ=P^fh(z4Xo? ~89o5o Me㲃.*g_s]+WG x[ 0E$ӼA"6)i .߂no@^h'iE2RF^6[l\:m 4J!eD<\Z.u]T<g<2~gm{) uK-BxK!Dn`ϐH0x1^T̰YuXFв# qvhāVo5&j Ϊi1z/cZ$E$'!g;\}o^?Tq':^2cVO c)XD͚xK 09 _DЛ$yՠMJxg3aF',1{oe0s֑Lp҇@*l;.ZpBeu8+m`fDL1d:~/ugPJNz8"u-c>DJNĊ(l=e.ޥ}G xK!4qI c<oSJA$KM-D @}j\KɆI,Ӳ˱TZWSF x=0 =ΒƖc.D-Jq7UȚ# aN%}3etXf%)"Q(0عIY YMyր۽W8|8$=fd[ڻAXҶmC+: xK!E9>cR*hAܿ7y͙#g3OE\* }! I4r ֳ 蘉0 . 7 *z[ƽ߿16hp)^Pu&[m1R_}DŽo7Iΐ xK u 1x<і緉pf1*>Ȣr<^&:su;5:l I.YS<\Z@E(hgmkpu4\Hu]UkfHomWP[8`: F= xA! E{7S` &ƥh( wm7*PΆ{ SD6DYr(qrV'w]x4#*'噊+*>#ZO_:H ߞ+:KiǤ,u j. [[Y{@ʲlTx ?ś=#7|<_Ġs:. F ˚6EX aVx=֤cn 'x)7(w=g\\i@3V׌4"_ 1ӣ&4l(r S a& y? ^[ xI 1yEɾxwtg!fRu9@9Ó2'mu^s"x6B( y1 w< yxvgB K}]ʮhg};¥'>J}ѓ9AF x;0{b{{ 8`vdH\7F,A8ubGYDX:Gq-'aC&ԊljQ zK0k >R;)m"SyAY{jJ'ù-QZ}/>( x=!@SLo,0$3Dw1 ys 5DJZe_I-r"qQT8%Q :XKFtAHHkH#7>ǭ8<6.|3ҷYXN&l}a = xA Eb& 3PcQ`QA5^y?z)`]*Kq6s`$HVhCV=!۶/p0 '}LI{<1 fd^0ۢ>k*=2xAj0 E9`;NjC(Bȱ2#8vMFPID`UNYiMQqFhR lzaX@8Vy'Thcn0dFo}''^nkfWxRWCgIjuYy;x:x$ӁNޒߗ &ScJOk 7>TΘ<\XTΫZJF)3! Np'*u]z?)`,,WMPa,)~Ex1!E{N1 0@b,0J6Yz_󛗟7:3hd3:DR2ޛ5鉼V)EηHNNeMrMNKF&4GebD'qmb_moϠBD& #RR^LΏ=S/PK-5bV !VپW ԔHx=j1@^]i% ҤHh~d +#B||k!D$%!3")E shG's+CVOAZ4rv&'qI>qn~sn~ZvP^3l=|*+=NBi4:un\TcmL -xA sݬqRUV*l66 wȦ[r1HaӶwk7nn8 *chxkjzB)]1x:DTGź?VF86>2*2l%EXw hoωw(oPL`$P>VIKd Y <}βU,/?O`8ϐr>8)RR#%X<[N ZD &' N9@h&aXe0bV {H_# ۝9HEd(4CsQ<9CP<%K:5*xTX?y,DF(]@Q_'[>!q;j .tg 怊 LKa|Yힱ<\-OϠezqYT- x 1sW9}K"`o'32A&`fxM.g^Xfb,1CE}$U(f[|->\Waj5o}:~'rP|6'X `% :xAnC! JÀIP TDio(@f,R@:,:$H]ehYe,H0E(71PzBFo2^8NjCo1gm-GjN OX2xd|~(X*/) ]z֌LҔxJ0} klDX)ϤMJ;]Ozr.Tʞ $@;X+N %kV1V0GkG2<:Y WUzNLC?cdw-UcӲZͫhSa2:p9]a[!D nb)d`oK cO/Oo#J$7Bpz>Ï`|B `i|S3]h+BL|}xA0E=@61.ǴB#)E=%^y/YX ZQ6:brl6β Ժ*4RaAC2:ukeZ3Ho@D4Um5ʯg*EP=}qE -oc(:Bwزsi ' Hx=n0 w{@V,E]HBmQPK7ЭZ(#Y2t5`GQ5Gj75nԺ^BlX`4kZMǡ#5=ne .8#B]J׭R=<)yu ?J"!\89>(֗p%8l38H $eμM3=1Lx[FniGJqZ2]~)s93ߤ`~Y<YUd0 1H`EDkw} xOAn0 WB@XRR[?$nbi?Pь=HfFϋv\mőya3 h4)sxOO^jͨFif'7_Sn)-yEwmTxj56G6ΡA ) )"ܳMчp@n$8B[=-Vfc_|t+Ym`\P$_8dG K1&aHB6q xK 0@91i'.=$/1/<VU'yeB3LHg q9q0T]x1{VD*,\QBa]lhBӸpDl'&m2)qVHzWxYfXd]/2?xKj0D>EeU=G,yd$ HmZerʯhXI̤~ʹFxEj)QH֌F+"9aoRawA;W128bkuO6#%#47(A*;p^u ' b~X`Ø. %զ_VT x=0 @='i~$ ;PPMMƮ VUqbfyj,9"(f]D>C"^k*)*Qs<z m✷pL2 =.K: xʽ @)ԧA 02A l|6?)2@^o"@'K8켉$ V}MfwirŒQ܄6UO3yG5# z_hT|ϵnsz M~c_q@{/vn@^rЫ>3 x90 {B=Q2ɖ @8'`-vn1-eqb͘{ΎI&- wfIqHȤEϥz.R='7 Ž 'I('#[Zڲ>8@Vظ \XN7} x 1 >& H66,h@DkFT)Ԕ4yQ C!UIdp{nvHdd)sSH^ *Y80_v׋ON=" Bpnږe]y+=mo7 :|xn0WB~lb[8\H|0Ҍ.98ĉbsÄEvԖP5c5m]D'&>.ClO`6gecȿbu/H~57NC|הpE}-i觜frҤդYf;+91GO*iɦq 9e=yEǥuk] J7;<"r[: D^y=7bP{ i?c\ umvl=~ xERx1n0 Ew{Je2%R, Cx_ٹ 2vS4bˎK͇2O52:[b|Mw*c<j˝(1ulǼ[|7txMN0F9Xqĉ*q q:ԉ#{**NO%`ϷzI!Xmp0fàb@7Fm1b!5ٍ}熱0x:;cGkUι(x4tRzGy]Y%7y{H${ ng/#($)p '.-Muw>a/Y \/dy7Q%@ۅox~h9նawe xA EbnpHG5H/ _y7UI/Z|a"G,,Hd1;7:: !ڬ6&(%B9L,q>G s٪˯\"@0 GV[B騠 kuLw=E x=0 @=b7NjKĩG!0p 73TPY:r*A1!rHr{nvOTLdX#fCB,`[x G)Iol2nz,[{>N7Ɲx1n D{N]H`FJEXlm 6$CdѬD|D'E/.p$72 +גU<)7Ⳟqlu$UZmLypw|ԬG3!q_a(s^V(2DL{?zF k >;j۠Ǒ2AJ~^/RqxA0 E=EnRqHT* |URQ%bޑw9+gEB'@h^uеAO-`&R.$GPאF% tp,zj#,_gsAaeE7|we4WgNJFxj1 E  ؎5*Фn'}Ȫ^Q[\Zcm@뜊ɸ@Qtf$*7>=5=k#Mfhe5ŠI?4_ߙ ND=[rDr_8_HTq̴C1O r@FZ:l ZU '$F{XxOKN0G8 BHß֐ƞЖHeϬFf0 NX)INZ꾓?(^+3ҳH n0ANVȥ6I4zKd>>}8 z~,/$IWLO]my7H)DŽ AQ'.t _@9mK uk0ذ|Bdx kԎKKf*z>J}՚}Yjkf}'w:_ҪG+NB/=m>6/x 0л;` q80hzP;Am@Mf3L>E蝎b疷vl)Yh7(#Y2 Y׻l 7IT4"b]{ GkM'CFK/ [.'+wCxAn =}b<ꢋc7r[ј! dd8Bmt]De央%Yy^E>n[wOW|FY&!~[47R\x/06m!H)s>hUѓ|{:U-xOn0+0)8MiҤ kgϓ<@7a;p艺>h:ێMY mgam𢀃{G:c йHpK_׸ L3;v(sT5Wo8eT>YȶjL eX8"gϒk8%Qw |+q;TO ,RçEy 3, րʅZȊK$q XT/LxMJ1}Pt"+хW$=&v>x)`<E 3{Mtso%BUS,`'t0+gzB~V6Èl3峂ǿ j5xokyo nIX+<_(eeʍDAea;{i3}1!+喅!EH{[ H.e+&5bxOKn0I l <'FQ*[I(q ظ`; *Ԋ G'+$ybj ]i|ĮLLs>d ;0R\l;ñ>}_y˜-`X /"C>Sm1;sw>'\BlK{ZLEiHƋa ְq.ebֶ٬Q5_}@e>Ƹ?p׺)sހPFP4!ggQ_[Fݐxj!Eo_FBC `Lϯ~@=38s$-{RiJ,VeQQN7l\: Y{ 9 >85yDHHc~ W*?x1[猑<2Qݶ;KNG|2?9C_rD|! +xbY9@-p!A5%u7QxAK0s ,IjDPŃ`Lvɒ?'`` 7:L7g]V[/,Qwg,5ҐAM0Im\RY7!:\1o'Bkc$ ~[Tǻ'%^ >PBa95y_c=UXg}(%ZK0Q5xl6(+.ѱGbw߁.'I -B;x z ^;0PK.~. x91 =MεWB1. A,`)FU}P<\)KD&Q,<2[4HQBIƒ΂%h6Crv9MnY[c_97t;, 4~;˚xJ1D L: ^Gtu2dwKՃUR֘a=̲6"e-]Ig(1d{ ĶY>߷㳶{x>dxC&mZԇ沵/6IK xa~Ks:ƱYjaLxAN!D&?4 Οĸ_TcN5>l;Ŀ`?i mPǛ˙JcQG aU9wsOE}ZRÔ x1!{NAob>Kblb4G >z3|/Z}|wX2\!,5Ko-lۋTKoy~ʐq]JmAa6bݜxKj1D:EF~:ӚQzZzP:/j{ΧŹD1``BogNCr-),q9~)Pndu,'p6fw Nfo1T؅.Mg+`}0(_hCY`QMk x1 EwN9]lޅI+P:ҽzzztU"3.lɢhuYqb]0VAYcEd&^9*L&ǽuqڏ݀P͎ 3C4M5>lT=)|9=xj0 y ݛ$Phv`2 v<BK F H*3gHiWź荞h"fdU<)Vkع{])"y/x.0Oug&<8}F(b z*Crr&(GXKu>q?P_XTNv>yRgu 6–7jٕ x 0 w?._(}%i'U_/{}p UHkE[\I4j$ZFf,1(Dr' *SZVKTeAR~ќ:y 輋=0W„3n;z韺NaS0y4/۾> x= @Sxb~LT.l)$%R,7|s.bZZrlؼx>!{kLUjc$u8!/.|:U`qIHH L9zosʟhqܶs=ȗ x10 ;MZqbWthSWSvnλh$CLi31W$ĪЮZR.6\+h:eAǁSTIxlVhVgw LG'u]<A>e@Þ x10 E;4i*!8DR񁁝?ItU1KSF Vb`V -ݝb1I2jKa" >j0JE3-i1{hp< Fy~m:;[_0o }MX)Cx; D{N}$5`@$q DžgyҼfzȏ^N5'DJ  ۩.8!x'\^OYb@ J^S^<\j%˶_2{~"cV͟28mR[EFG0'igؒ4 }E-jzhQXI땵YTXY4 aoNȝZuvF<]pf5z6U}jS1rG*b ʔGo޼hm #;`;&{ 8){4/0,~ )Dz/\&7q2!iVXeTi O^J.­̵3_G& x=!@S0 3${ ˠk|WT-PpTeqJA!Ҁ.MnE(X<E!LyI D )d#Goͦdz7.q$ۓfmYjl٦mK./': x=!@SLoð$c0( xNy"$$k"HV T8;U͞EN+IXdL,6Xx(L:}϶?\m$y'ȶ,m k6WPټ8— x=0 @=bi~$]ځB*U{|8o7UKֲE$ppTFqơ̆7\LFNdKA=zR? 1"e{ 5;!ܻ5}j Kc2g\|go?ӟ xI!"n 1G@w2NUI!3eޤ"-#3c!X![oz!9(#"$T"x aU^܇k[OVrĔCtzg6)ht,{ziy/;ʟx B!TwAb|B5xAj1 E> 3cJ!$YJLapB*4SHf\ijü:2f 7uPH}GN-H̆dTHZc~ƹN>s)%<# z1%9|6hzwQ͇4ol |^ULH xA0 y\u !q@[T)`2#m̠QG"HW҈dVRGdnS6[:PMVX-#E.TчH4( jݯvKON|_2sԻuv֬cR[^>ƈ85 x w{bi#5Q"{o73UJB̕L}R-b<nfkh9aNIp05Xt}6%1"+۲̽۟ ׳o|<;ϛ x1 @ѝSw11Xzbl5jI"B.7U}1 & m  -QnóQV#*1KTK.)8UZĕk<[٪s>@ʑo:V^j[u2t81=* x90 {}a _H V H73;*-( ⢤Z9MyQ}@A}`ђg+N>L,+::lGp6&fj۶u S7F(H{Fgm SU@>xM1n0 neK6tGP$ը@fW@x ?nEr DjsMZM:)Jբ3{n;c%bĠE4)YU6_jl!VT\~Pn/.?\#0y Nmm kvk[`{y_>C<x;n0 {הޅ([ ,~ܤϫ %ے};ZDr!6/>,r1_tI"Rba\سK1ů9H1 V[/8^,0{ϰgSC~Se|fZ R>/fK;xʱ0=_q;Kz꒺С)J>EOZ(A8Zǃm}9 HQs %g ?e8u{>mjPοvJdbG ϱ :$]QuCnKҞeI7J՗ x10 E";ݴMW\)Vi?==ᷪj8%u=zaD7N$"fK"|XF"X!n/CpxdnkW`OpZJnMͬ- S}ЬfS//A x90{=X{#!by}Ic'`)KQqI9R+\ Zˈ`{Y&*m:@j.1Dl)ZCZ׸n]YO.R'c-KCm}yS-q.!k/? x= @SloqlZxv!x`3H(-Ia %F1֮iHkp/YB11 S t,mB8pÐkkि;5qm ϥp{1 U};x1N0{b{$7DB4P|GS iFwHJXŲn.IsCS[Fs4Z\(EAa\}nSVJl◞rdObn p&md}G*0?QKNx Dw{# $RqlDjBDeޛNOtALаcHH%{<L͎bd5QRԉ!E`C]9ާLp'u߷Ϲs:l eI=X^Uop_mFw x10 ;K+!8D*!s 7\ofM9xQ)6 8i)HQlET9-+ eٗ`=[{V':z ŽoBxNj0+v Kqq6C<9ׇ^]$'h'K(2%ed!炕OȣR+J8Đ'C6͌s$şǯxGԶ2ӿ/ xvc/b|<qSd$~Oۓx; D{N}0^>R,% X"OSbft od&18@Q0F4‰WE]MދH"Qi֡Pݤ vWk.zFq+eCUp2g,O x=0 @=$nHp;KAQIMmNj(+pH%262,Kt;v_$٢$P[8 ,\aDfstvx~u (eD -ܻ5;;M]}X:# xK 0@9$|@ ^`LJ[ <]x{&+2M$<8ʉX䬌Z-4]:H% 6*޹|2:hkxNˍ`lL!7LYy] e0N7 x9 {^})_8MbxEɎT7Xkt٬xj)ʊwZ4`dBfM6lDRk-\pQi}&<%Q#:y#Y`:ϭwk6XKGhޖ ;^x;0{b{7Oc?C8@AT#8)} t5H*Qņhؙ0 9r :"E E0*mmT?;|`:*N]5xSݯTGj昐i|ѴoQoeED x1 @ѝSx` U oGWPJsbkH"Ӕѕ.]^jDHM5Ts FĒ 9ޭ*[~42".&u?}As625_s*Ax=0 @=Βi$]؅HmftaMTS&]"JK1瘢(|(,a%&s:¾C][8o}ub9;z9BpW}onq*n!Pbv1i Ԅv/G@x=n0 @],Y?@CIՎce?z^U!D"%W+!ë!&QGˣ]TJjf.Ca֌}*~n>c ?|w:;m]_ֵ]* J5nΧD<48F x1 @ѝSxDzTBDۥ{Ch LhΕj7WfR9yȡ& ~E1mR̆GqugCpLnf9Y@T1ݟ{6B x;0{=nHػk0(JIxL14{H8t@+9FR"+oڇ\! gZ$>ZR=ul.$?''8fx]6k~7 x90@ާ)ocGB44t 9Pp~3(/phh2&mWW4礧lFI(иvA4Ȓk2)2Kh=mFO8&6Ww N 9K4.KŅ+0C96vRK2쩕N>xn CW,CJ֏¤"n?٪Plɲ1QFqfBKHdώ~8 5*tv fH] k.8G^4_[ 9 SR=6T[{y\G;o#L1>bj|Fk H* -- $R'sZKqD\7 x=0 @ὧqR q HRWΛYSL19 L?{ xu%]H` Ƃ1{NČ}F"f`h[+~w:~rɿ~y)Oؖ"+XӗֽAnxj0E{}E,Kf@`MF/[8r&Y^ Ri4\GCʣe[ f j!IJUp^d<Ã=> xo bO/ۖdv]p#Vг%+߮@!TjА@/p[]sz;: Q/]Wېx0 |w(q,!q5iQH~sӝtsBTbIK3 "CJHym!9,5U|84d>Vtw"0.p'Fۜnʓ[aY [hoSDx Dw{'&1HUDj"Rd[{7\FX|I XYjռjn 94z)08GȻ.9j= 2#Mp.ʾo韸Bɟo%CI U} UC GWaIJ՜ xK!DIK̘L<,<ԫת1h 8)p%{]&Z*~2!.0$LD}uzg•Ia(EM#S%Q&rօMUQ-lǷ6(t^>q!c?vԻCǹ} 0__:ɣE :=*SZ^NxKj0:E3}F!'CK1d2oUPx:19@j(ujT&x1;lJ'rj (אS}ʛ`^~镜b gKO{u]Tܼ}й +NCy}uukyk}OO x; {Nzfࡨoo V[AUPQedRsa6! X_ EC$h$IJֻ-( pK#ֵIŬQjP6J hþAxA D AFn{~SDE ~- w&9#.Pf+} (w0 z%J)kţ=_).?F[1<É1_=Nٍ25oKjp+ހ.=MlG xA EbhJGqfMMs{K l=mI<%O&06dj-LL)HF$g}hF{}PրRܨ-\9]zO.eo>0*ka_`7Ax 0 w?.2J_%ȖLRhsJ3yHpS'DnGS6c(}I1!;m4t鼟r?1pDa).'nIt<)ՠi^➾[mk)2mޙ|o RCxOAn! { X@0 - 2U_.[-6T! 1>JܒO% -d%= jm[jDX 7%쉔jԖ["Zu 8?S.:n%l x S>~]ٲ'L\iֲ~(~Px0 |wHq"hZM7!\( ! k BPr)bQ4ObjB6kL Z'rԫcn1zC.EmN3WSZ)Pe^sC} Fx Dw{bRq0(T@~~Y:tMO;לH,8 OhQ<*:]%#Z2OP)ErdFEN.:_2$Xg]͵p&;;!t{3nU&h]ՠqQ*ZJqx; {N}R. <K`?N> (c604Gc>߉Bٵbmd)P/)1;r`4yѣJgOvB7fҶ,SknN: ;eS…LǞ|F x;0 =ΒG]µQAJӅ73_r4DLHTJh!.l>G58i)JQ +㳿^ڱOB&$-\9ٖeݹ+wO~[?p8;xKn ] `1L+RUIUk$[{ :c.F6Is. .EBR5_CV{}I'w| 'm:=#=*C喤.dWGb/LZ˜OtgINi[,2z}Vq6 ' Sa8)vV x;!@{NAogxv,k=[x_9D4VN`,.Z"WX)GKgҧ" Vt KP܎j'7mM̹s6W;-U 8 x=!@S00o!ݟ {m}W9DrT6T]Ҙ2VN$;ZjE4٦M%jJ=5+IV 9+o]{ןn\F d/i.sʟ9S읟K< x=0 @=R.cHA!Λ!aMZXѺ±M.R̴EV,% 9&(idւ+p\T/5[Qe<;\~bsp }osʟZ7~h7vq? x10 ;ĕqchM7nt3Tb1^kP VLͩ:Lo0_~8:xʱ ᝧ݅zĸ8%OIgZ{B2)4%2!5vn}2좥& aB;Hl-v<slw'@=:mC2Uj_svإ14~v_!VM}E x;0{b{;q%X8'`)F@6 2qp"63N.NM H`u P۫Q?::BbudzcS]nm2w/{?ɑxM Fb. O!1^ tZQ[ &%Uf( zYIND !RB%v5 2Ʉy҄|Ph42&kUۣTW߸.D5,}KmSJ 7QwÚS-_)>oB x10 ;KI#!Fan EzS1;_ld Y)\JIb6/jv@m0"*O]4$ 5O.lLяh=)۲\[5@]sg;xAn! En3.Ъ,n,K~Gg%׉8ްsCt~>q%k7\V:yަB$Ҭ=G65nA׸JjO@n+ |)8&r(5~@(ހ{K-нqI6V:z~ǨP;V-cZQF)QV~a#>孩o8O x91 =Mb'N"!Æ$<)FSE{К*5$b$+V ]leezJnVkCKLd{1-W_\Y!=0mi k6MC7ґ x10 E{ĐHU7bҊPpf?|jmƦYIhe":bw|q[]w0nʱ[kl\A})8> x;0 @";%Q؁ڢ4p~:p7TPTr.SAsUsƘDɼ:cB'{SeпbsQ]qi9 x=0 @=;]ԅVia"DB%࢑ dp"9rŨ=wiSggFL#ȖW?_ ~hc.*ۺ1YU^zuw([{K?粨= xA! Bw/$ Z.4z׌1i]ĘK(,:H=]ع;SY|e ski$B*S!TV #r׳Ofza)4nMa~v' #\mY hvk}@i*l~0l]}zH x1 w^ 8q{ H{oΦԜ㊻0/+6^b襄+٥=DŽzy 1+ş}fg]M |)?. xK 0EYۀ!6m@D|^J4mJ]rl* ld\)'x?NˡSȼJz#!,jf(NcXж)ך#L8?a+Fvw.f|칂r>3 |b^APA\8 xM ]@ hb]6(Si^jd2bVPjq j`˜::`b@TI<=2ϱ'X|:%oTj-9 xA D_Ғk=~QOou5̛n+*N= o-2@Ql?_S9 xA Eb.Эii z{{y|6xa"+4II";F{‘+qg*p /g[*⸊柡S8z^JƮ!Ħ)ب (h?^3 xM ]R Mѵ˞kR06m]M&o*(ʚ,Wr:Q  'B(+nTR.:~ޅJ_z]Rm+~fԪFK MCubఌem'c d8 xM0=Ż@PHѵKN?H QO/L7ܽ-`:Zΐ NaT4QIy=#GN6nћ=mC̥dp~5Đַؒ/"Y3TKwpR2vu. 1/ː D8 xA b?P $ƨ̖`m0SIcQ*P{]5XۛqC$-kۋ`,˒kaJ3W\s}Da|9OH=?C79NJƮ1m!Zo%$Mwm=y'M}bNy%طbP{v^F=as!:wxDw˪ljkNZW!ԭ4lZ )McT.=U=VdVmFY!̌u[t&25+/ִpFۄ]UzY³A/ʦ}OuR3X% q:Z:`_M͋)U微 VZءyTuc6Wu5-C=I f/M#OS ?a- U6ISH6]ʝ>|?qeaBYV ̑mś 9LzUސIbz*ks(6վ9M;UG[i#hST]Fo? {Tl縞<!.;P6X'[6V:j0:}iLa=jkخ8vRZU ks#-[ZZs91Wx⾪5h,qMla6 A 2D)C32:WNؙ36[u#{DpWo؎Й|&whcU7NRHRDvp؅4{ VݹȍqeT,)̂(,\n( >[2 I_=pMB_XIO.A#H 庒Ugj(0 Cvszx!i:pΉxOb32=N6 Jem[ZN\@W,CL]ՎY.5TzbSS6v` @'l[]қh*,ȧ2!gȬv&Y ]դ3:AyMzTPP]w]R!)vxpHL9|[k{A i[@OJa4#Uq̵`θlP9g0T^Tl\cDXA|'+; sOrfPyؠ YHznMJuo#PRavl#, CpQʀDx*P 6M="U΋`w-~ V!\yHV-dE y df6u2v"lSٷag Q똗W8\WernaMGxPvb`Pй1B%s}jЙi jŮ?%'1 )`R}48jɖka2{jgaG#%$#ֺ@&ٴ瀛Xa$7~!cPϼ;<(Wu Jޗ+~V؉z]Od\wG/*D!k3hрV {_0`]-G6;9(v.[*ix3|<02QAZY*ҏ~*sODs; ?)//g \8 8sA(Ma:vgt1-p1d+̺n& y.n8zSΘQޓe(ɻ4z% 5Ɣ :x[MZqл/ݵe']ƞ\lk\Y+7~Dߣc횂Rv|N(ۍx4g*pbEPvڅzԌ\a 4stq7Cqi&~U'N8 Qj\P%@z)jmPN\\#jѶy>sACk~;r(,64RwjDF?m$Y}I7{'ont{c@(N$NfK|vQBWq8_MLjix^*~ 2c?8Bl*A/t0TCcC_ixO6^>yzLqӕokp'/&:|pWf Ἀ_+@sLBw$}Sx%67<\\ Uh(2ǶէzhyX\ cxxOww$oUS[ G6 h_ Nh栩 pGsa[íur&H|4?a7 7R/Nq]d|eϺ ,tA^pH0h NI"2CjO/ +rHp>75IE/,gހ YYa+bNώ5\W'cF̅_]rRlulg y%hBSf(_HTs$1_}Ω3{qZjiL]=JKS;*E?FWbtnہ\hDFɋƒi|uO m,+|_ qsPcF$uC bnO?5/O^rĉ {*&Fρ:K/ Yrzm9b*?3=Q.o?Oz¿~f#$RR48LL':,q'z VB<ӆou8|VH ꖣPu[ WɮքJ{;{Q1:#$TMu<|j[f9eAQJW|S^Ի/ Tt6Nxpk(kU1\r6|(Rŀfc~\Ԯ 'sqQ)U==W B=}ԺOOn=pROꇹG xV[o8~ő5iTH)Y*t-s aBLnm>;߹qƹx-Xp=P+ ,^ +x(%g?&w9܂5xRBin14^F.4뗇^\N櫻quD|~.Dȇ?d6-&}8}x?g5ժa;Q7Fo:ݐ^0!4 =j[Vnێ{wD=7$ PH@v!cSrYH ],GiHqden~bvLmcMNԐc:)eW7;i؏m% G&xsfl,8v)7&}LL6HD{llܤ&얤\jvBm Y(eY E?\g964V˾߯u,`F0.7Ab$P;\!0W^L{kѢZ<Qf+zxK5ڂ7|z<0SQp1x![-t!R3St,K]x*pLxXKs8W&$U8,Ov5]~ԜR Qؐh~ ef["^7|mڃU|>{7KWF_uNi*}1sKnNZ䪮)tN:iN9ZSYysQi>(]`#`Rqa% Uӥ͖4 |Z*vٜ?T(xɍR9oUޑc񙕅7 Ps׊&WVA!8ZKE^Z6cH$ V׍._a|q!--|e=a謓nӇG6?X [* aY_/Jw?J)jy1]\,߿ 7c;OYKu2Pʛ (J8EY.op(E ;0z5-UgF )\riD"ȑʚNU@]q;rᛛ"4nJ&mt& ձ/; x} 堺zN ZDq:&bC(3<߫2b4,kK2fhh1y k %*&}8 up}!1 ƈ' 1W'фP9T BopZ\OAacx_LdL>5ZզsSJq2CPȘ`:w^zS@?'Iߵ)vS..B%|u}hOiaA,Q֦#I8;(";GlUUN#~|18'51yAy'ϡ7 Qˡg2^\7fp r;~׫ñ]ejzΑ%Pc.K&p4saO%\>˒yVٙg⁨"Ig&ԨƖqHi6.@P#=mV]BCʼnx"W,0p€q;G۠DDJ#w*N'# E4^bʂV1J`3 GyzOR&c7S64O)5@Q}TXhҢ>_`ZmYcG iDC*Đ Ʒt&`v".t#o.fl9_,fsjCAxûl>_dˏz??S˳M s*9% 6:v|&] e/,]x340031QMNMIexTQd=^Y䑼6nĈĜbd_[3R knOӓ %'&gU7!c"A ^ʯC)Ч3B1[,] RҀDy#0eyY*w* 3gl:~Uc,lշIV"Tg>8A5aMlM)"9:ڎۛ*x>yKQ+d VkfwV쪬$ӭ(Ps-HUC\Yr5N)R<$9]~G$%rd/L*Nto/r by@8`t;[Ͽ/v4\cHk_鲽jJq0zlQR+1Ta xJN8U2E䖍ki iCV7:k=5(]\_Ykch Sa \ϕɰ=;tOc_Y' 60zx}Umo6l[ JvuiH3 $")S8D8{ܑ{k̀v@jBNg*R%}L8HK#U* sR+Vecµ",ZL]!|EYŒY +BBmtnX#0纪Z"3Z1,+Yi1 &K6/4*CQ* :B&wͼ e, ɋm%QIuF:AQ#C`ms 1_XemҔ$S1orZ" /|@t07$[ o_X*^6x4Ҩ٥ss+cru #/3.$[yRpG+WTB2 rTS>/ӺLX @)BҩHj t\zmpB2tZQ[tRhiK!Ld|s5Axz : H~:<ޘ-~[y,ʗ姓0uDm2-mm⃘c_>^lvr` N-IS~R[ؙg/ {أ/k t qftBۇ>+ѥ BQ1 :9O an;+l:MKQ$Q7tDw>àx+-PJaWbkK/ EM,[ Y7ܭ:OlIȊILh/>OfZtt-Tp&az9f3w\͗/ͅX.3`xcP8Ze23UKofBs>lF?6r^>![Pz:YmD!>G#v>~+H>t^#'"r8MRpzC9Rσy uO`3fLK'pD<kA 8ᗇ23(@#!= >^O!z)܅ H1Y|pw\%%AkvƗٓLjZj)+HSk}GFf>?mnYSU1p6n %* %.2gabMs/`v  3,I{IZ܌kyn ]Q c=!ki`P6?M(tP_#=cF۽ީ;;/W~mω;qh0>p}ys} ]zt4AKaPj?g_?>g h=O{ge>_O$]<: l7l]MNB05y b-0M< &)xS^ ;-Rn~xH[Ö<& r$b߳2M%V_ҕ_V^K踐K Q*8by`BP_Lp8M/JC=L^n¬uL05y^%R&}O&xك ;pAG4%s>oDEyUk+ڄ.PףX 0 *Lir tFSސKn)90W))^PV$k_Bk Z+P&ЌilE#a :1YlдWp\qjWԺ9#:=׸s4 V<cihA ː'k&C*aeS*'-MЊeS\9pmTL5ٙ~v㽛)yѯV 985vj8vQwħEy,́L)㹏^hwWSkut' <4ƴkqa4K.-Lb؇(WcK[YlIrцrܕiӒU6q\(ELNFXPBфK8;SDh6C*~ˈ!&ܗ}owz qvve"O_֬Nozv 0H0eYl$2$ eGY?\6-Ȓ$# ʓ7p(XߐDV juld!8JxSjJy(mi e$Ka˃+UB.%i!MD('20FN :rD0LeY2_1zDH4Tk ׎;'4Q#}{ !WalNCkkGJʉ29c9+P8<;"t!40+CHia:20/ .\ ǚnXc(I` (1H)ƤJX+))ϣ*nz|"7m״>4[xl8>8zs*⥈0>1F5=BI8jM[t']n>`am5<_aQH؉8[J}]`!S7PLSI`mTy+>dzvn"Q3N#5vm{mlߚCIcQ sX(T#37C,bE<2bI3Oz{z5ɑ^d@"!5uZKS)ċL]ÿ&P}f~21`O7`/;ޭl"<,3HU"UFC_\USeA0{smg-ڴ~{Zrт5>!(d3w7&\+3gkvV(.^`Y\_n]m>Eh07֮jk 6J1Za^xjipnc6~u&8ql^4 d<q"^7 4ai6H6yѳ!/ =x>xfvk7<ۭ,5d7^t{Lv.!`ZX1ɒ!Y @>OK@f6@Yʩl*6435B ԦW"]L[Ckd#Iys#"ˁߚʼi\z~;[yk/ao;o+mZ켭66_y[!5 YedA,c3(9)e3;u=;!UrQF\FZ}1C3JUh{C9rD\G.r7ŋCbp{f7]a_oȟ:LmPk-0ԪMKlRAwBm3v=FU/Ò |,6 JbNLT̑ʛ:po˛ogW97xK(#D&[7G\q `ި93~1LpI>uG4UE^}b[~?W2M: _ lOO_shzYoL=L\q}Ԣ Z:O,t?O]fZ DkBTL3ckK`3?fiY#-KkҾ Q{~\!ΨWjU2dK&f3DhHx\h46)Z`yrIAQܴBhVL#1QU1 }\.[3af"OL|>MguIwup5lwWݬwR:6Hd.aOmʄ{p,j%r);a9@>* i{TD]SjxI\ϫA'Dopjphi>u*LJV5"8MflӽG8 N;5 J'ZiS*I_MyQ* 0t]}ulJ%^ތWaKtI@Kګ'W+^Gfn5q$Ct%c+ʩW;{fA;5<<گ1x-CfǛ0Y=6t'}hr.Kz,ћV=|i_BgIcxSa8Br[i-J[+ܝNbv C׫T3y8L ^ٛ([/,|yA1p4l>_c^MvNj\Pքح;PFX pJ Ԗ12f-R1XVRZǘnNs$aPTysӶMQdB;iVFPI"wp鼕.tȓ$!=5rk9g\l:{a3`My<GG>$}갽F&x]v _%7&Jna-5㘻o8KPAj Uj4NK(u|(O0,DY2``ft%Q X"pd07KxH}H*չftH/DVktp|~JSr:paYaYJV*)G/jjc8>"PwVãjyyGMV\U<8L=:_&˲La87⠍heWS솊VQA+Z'HH @7U\!gޞ"XI\dB>RK<V5YjY,nrt,O!Y],WoX7Y M SXߋ2N 75,Q0֖J ^vw펣 CxnZaz5?.{ҏ|gNC_{Ix{C䩌6s31)y;*g)mef*fgxuTm6  6_ɭۀk; M vذ"ɶpdHo}|,C>|H9_,`]-A85Hhw[! V1A=wc^ u Yqu=0#F:EFk((Bz)2x(cmD 혹S):ŴG4ߖ)\혮ЃȄ e:y*\:q޳Z^[P-deP/5WKױFIJnM¯Xh(#ʃR~DH I&4y-3„O NܜI;q}JɿO Ǭx340031Q,(J)-`bt#xڰ>CDݞ){y% 'vu=Uó JJ RKR7>%GĢx$+S¹mVN-*OϏh&ƣ3+k@ &dW\R)  0dS$tK,p[)مu3nY)ZpK%4po#Bi1Rj[ƗYuk%ὒhay2Jꮋb>(y!jgQruc8ޱi>/j^u)|Z߬c N5Jd$D(B**寓+\MnsRH-;[)(&RYIЧi=lл 9DŽ3KgM"hK xPNV l`[ǐ(}( 3i>|yW;ei|{S<<wiXr湦y2{P1>=Y00KxPX/'0( g?sQNlϵpwhnPۂ)VZΆ^6ă:(HɷwVzEMfKc^g6ٷmcR|2cN}TZRݖGw]sRmEqߓi?fFNw:PƏUen` yG"{oh{3֤.jymb<9ܭv2q,);,ܖw8Ab8r;[knY(~qN?E3 %xwo#KJind;Fu#Q˜èYPY20[qeqN~W 0qAB&mcKo~qRf>P!H$DABA3 $=5D3s Xd=H.+Hk;*DXfTF:@_xTMO@=_159@%'~IhTPB{*^YS"wv{?޼}3gT&zaռpu kV %64g! ?[A +R`.\fnE !jJ9|%ȫ7B"- Q LA{U&-S%QN? %[v^FY6^; W;C^'oS{ uUZu]"f rX9a]SIjiU2>@.*[M8'0/q|,ܨ ib]7b<xtJ#EythA՚)A X-OlCESu]x/Փa^*Kf DH 9ZV3U X%k&v-BX>02M2<j۩P}y mit٪b[ǃJhZb7t}4<O&O[!9Hn{[]*핔V.- 6E^0@!it_0 .'K| ׃R9|N ^rhtIh1`I؞\\EX|f=Ȍ˝<'N h#宩æFH]svBOxcx{V{؊+s7?a`y/ $Fxonɇ؊+s7blgz wxTmo9C!zR)݇oW/'9٬^4@ҩCi[ J˽iP 򫘿g0=^?yRHZ,ǠëXkh+T6]'#a@+vq6m N)"h~g-p4b+;Ӏo%[VtGlzqyNqy$0 r湦y2YP|_BBոDMJ̥D9IRi 0vd^찶_9-^^8+Iޡ;,]];z|gWn`Y0o{{'12|3 Rմ>ßF+[4;\p3(mڷ]Uya?TV~f%fɞ>[!S I"QvMmvCqfj+dtWǛٍ'KoTa滑!/pu5uJ{peE;<6۴&.Y#nG[s\m(Z/>oI,pK[J6xX_1+9 IKJx=o׺85 $R)$W( Mf\3Kǿo9,ibY};g~T$y(Z? yZda.3//ga\ltE$(n|"xGyy8 \$q(''ir/?l|OƮ1KC^^d]-J0 vՈ40]v*2'8.R/3f@ -Gm{ş&jꏡ_ mmEk3lihc@CtQ!lholΩ%b1qgbY& & h|p)w><5pջ:@ύ G^\=?/0<8GINi%L/iô6&Jg4qFT+tKN4߷ԋs#wڃ?lH} h'dE>7M| ԉ @`ǷIf2W$. ]xv`5#x&y!-ֳk 2@3 Xt"ӉIW>i.]N'ǘ&m $qHYe( &lZa B},J{-0?sÎ(Vvn?)#Ec??__jS͒Rr>EEO{g`0A `nPeN0 7Ǽx; 㪆Aq(zW]Rkhi е\c/!&i28FohXG9i 4AJOI CȈoAc/eЧ@2GChwl`Y"D$K%xMK,뿶Z&+bBKt݊1ZH~_ ɍuQXKr̲qUg4TA)ZA*CY@~'eFM=?.mC1I;Gj\GԮʺ*ێ{,e F$lG5h-%w]7[)v-w(iK$\J%e4( `A } S@TI҄KVhsQp8RkXWZfrMSr] * ep_XvO"@/ن`K;KczN3_qޮܶ." tOhFƒO'NRUyE1'|01ipqJ _K$[K;8ccRW絀>U Px2eïߩƆ9jȴ0 9 ~K$gi]t6CµG9X4%J\/ţ.{O}a_ HV6T|4$s>ɞ e^EB"/G30I4(f!s𾣛&e_Q 04,?Z U0 #i߹xq؄ J:JB=v +k9)f'̔V'L{!QAńPx!)Qs.s:8a̳;Ɛ*L5ֹa*KY\1yɛ()2^$pAeDZO>j6MdgBc75P>ZrS;GD+^,@D<4qwFbA ǐHee:twB8گ:NC6d~17nGULwT bagTv@?;U{w NNߟ?82)r7ީ{, z-4ҀuUP9,03T.VNj`upBf ;7W Ȝe*YD x ѸZ tJ(~x~ZQ԰`6']Wi1135 P:Mcq2-p҄駟`~O~P*Sz=>$˨y_' Hk-n8jWQq%Wȧ 2]޲֎*+G7N<v2`Χ)>K2:!|? 0\BӔ9 oDD „hf)AxQ Ls@7g:,95GS׀𮐺Pu"I1 lM3`g̭6&)g7 {sfHOiQ95ȕ-J=#VdG8~Mac(uUɽ危E|ęTnDJ5'"gA9N<.3rsPK i)@z.拯}o!w2Ζp[vL=+ײQ +y} `K.4ֻlHVU?_d Rpa?I+ ǸKUڜ<&q}ThVU6 )΅4 79H-Y 99s.5c+KtXߢ]'ar3=.H؆!KAFɤ c&i\~ Tܥ*IAgֲ ,C#;NI2`гH*Eͪ %GZ$Zc~!?f-QߩYjaV&&;v6eqt{xK8\,׿([,eplVZ,uHl\sv:DO;˜.ԛ(v=jeιzԯtˆKK&:TГZq'LI(Ɲ gƼ_>okYej[}~xxw[i5i8Q7 zz'j|w ~/oQޛ5[`?=p8J`4'(_3=lYt6'5{d/!HlooKKΘxX- 7@2~'zBS:ZbNٝ%8 %sV+v" #UɌd!ƚz/dfE2*]B04pۘx VL*_e&%[lcJ/n^?QnS')`UMm𕨍Ma&UޘNyiXl'kGBo/jyoۤeXKWnKQ AJm͡3ZA,ZʫcoUǦ]DKqwcPEs z荏@4c ՍQ|gEsЃ=̠[%AXId8bj\@. 2dȀdzTaxt0ƽJ~o]5ͼQ/j{j]yiع+/ %^.A:9/!ria75.Lk8ZuoDfl㰔u+-Xe~`^),o`5 #nmq.t@Oڞ.t:Byp.`$"*w錇0c=e~V_^H]`šͯ[sMS6߂nW' J8Sm__6g]{t ܄O:x޿? \CSA Y-m?/xXklWƵCN(ۍ]۵*uZqgwggv+jՖP%R( P(HH<"T@*DTVH+ ONP3Dӝ56>:zͦ*C-ŎQfq:*1%L=zz.~J>OrY)j PķCkJ\Nm'}BmQ+ _*ὶax:(i_0!nZεĆO!WGgܜ+%@ (5cbE0TS/dA\$~, 0&rKLGCBd }ۆZvRu]aIc]##+%(|)D$ŦJSż6+<2)ёAI%T:ABf( 6j_+a-ǘiN7dS7ȱ,Z.wRH9ںOZ>Ϗ?`{r{g#'X̕WDmD* Fnx/P _'#SmyÞ#! >ΝaΗp|D#sG Y"dw VkMOp ymbz0mI"]=Dq,D6=)ƽc-]HӉ!_C6~U^yfJSs=֋YQh llv0ilGwl6ؔcã\G+0ᛇ8:?;e|<ڬX߇I;O#<ʘ؈VTp td?kWܠ'y޵z#pkng[8j3ӗX73P IG閦-Ҽk1.GR-Go,g=v9Й x=ms۸+0t$nmzst}ę;{7DBkuyM;ss+Ko@uջחVPONek+|̮PN^$Nr//Suz_=JN/ϕmK7OytT7V"oz}y^]'SLÂJRXs}?k:7d:̼oU&˫w0Sy 068 oSK&"=d=- ~ S`3F9Trۺ6e ER%ۓ:+]g(M"7.R]49DuW KX GUptBL$HE6uU0x%NVF `Z%GW^ڬ6OUJM|ܛ2#8MY6؝8r8+cs8SNmlՂx(`J)ా* 8:EhT? Qj`]4䨗tdG9L(Q K;ǐnM: ! \(t`1@0i&\9yz6S?WYdݩӺ[;&Be?@^=#M+-,jg^yR-jvd8735fK$2GaV-$W"Ǎ`,02i!>49 L632xe5H;_)hZ |,n%KLu ]l݀9+$vJB8_ypD2UTHBJ~#7мV|``^t-UW3/$NKY51D͡a _J+*,-0jBa:ӽL;y,ml.>sV\GY19[k$USZvvѣKsч) M)uِe(=U0\pt. mMY7Pg_HDL{KGyv6B[on fa8(zLƫ\Cb[]$ޗƜvO Z e's6"0% `c#mH F(fiɚɱj7:O*2l6Җgׁ6eUg+3n/JqR%c%ī:sӻe]A0\ʀ-"#b'6 'Ⱥ ~eFܛup)kcoa-A߭ղƊE26HguUNg0Oa%D.jPm> Fj\=x#whRL1.ǿtZP^\-;szP9pq}'b/7gח\ʢzpB;5g#Xf BTF2XzibA^!jt6"mEZ]?Kux]p eEA~ ƤZ]^JH)$@()輖߇D;:L~"xv8wN "Kp=-4 vB>yȃ5Ro66e9C j-.)-Ǐya"345Zr|!  ޴"V8 HIzȈin - FLaj4ctoF|0=8oލ x4ʎ%l]1οOȿ9>:1E U\r+z>Ru{J)ѥvZpI >,ow=%;omA(6ܶ֟uA l O0`ĄJ4GydQCP*8h/Ng1Z4+h8oF $֐*dajgC5q &s Ro+=]4y3?ybJZ`w4e@'ñ3uQ4W-B gI9w0E-7e:CeR <^G ZGk l=?( 7 #q.7 X1?֨ѢeEvD9Ƥ/J!1?`LnNs1ȉ &%rueɥFtҖ>M!GՙS6 (ݘM(bJuL]1bD4EiUgc=_RwLL?;N4ˆ\7IOX2` P~4(bǚ٬:$!I YhC40 $wFŚ+\':Pr.E\  eĉ-ArG6֠M`\"v#y>D{^zJj@,Ўp)zRx6]̇X]~uyb.?xU0o:Eu Kf89ԨBmDJhtƊ1E|ݼ؅ =%i1R[_'1 qoy` A󏏁LGWU3hİ^ϋ)WʁS2YJ8wb*%o0ctd_a2ǮD]Yp5:ѯKꈤMڍˈbΠhץ[uGTu\esYi쿩JVՒ:$T AV<فkD+4+ͧ԰(Aq-T!r"f>m `Q$15Ayq.kYr=c;γzL qy](3*I Js>TzI߽7-PHRA(uH R^[}e>YM" ˤO){'vQ%1VӇ!εSSxI`^fi0ddD2{W9Dy]2_&eTR&W`[tɂW8 Td|1;OܹxهAV0W`ꄀh)HW/Dۥ^z5 #g@GHVװ^Y=ѽ֟%. +#%_T =<}!t{_p^+^ClZo(ISl~ܘ)V ~uǍ)7㚔 qU!T⋠[MvS> i;$ D~%ǂ O}g)nq"WV7m1>7juPfŵ3M^ N8qh1Fde#y0i5Q y0/%Bl7DK4oblWn< S 7ڠ'%1 5]9G8;uv%iyJIXZ!(@EJ1+wdud#M۔eb0NzXSN!# f_IgqZZ:1N܎aG26gⓉLjJ|5M#}U&:0 !XkS1.pkٛ͢TSd]Ks5ͽ8omY81 : LaNwfi̇<HZ7q#7Os)>60Uɨna qaG'H[sW eRʸ51/z daP0O_* )2Z-{S&|p $d>3K7 QCWϼ[J&d< `yϏu&~鶇|g0Jsvޫ4ڣnHN|t@w4HJ\1yd]LGX2 D+mE똡]egKY>n+%qmk)|cEqMG0]UNCxQ6 u玞7v? `tۤOQ$bjc')D' 6x ( qm9(=oHvV4į!~_mc$u Y$GbvO bj~7WKߝi%g_PcG2ɛ(.4ƁuuRCē:w-|-ڢHTm|naH'ODB:i s;_(rC!{"cCrnF.b/w^*yX:.]u3sFI;2WkBz[$\'W&饰 ά|$Pxl,zlvR vx }T.M$Q[A!/G˰v$<2PZ{ Sf2׋o$\-p?k@t0>ad>G:Q;x%nw4Q%Gz/C*ُ"m2e-N,~> H >]\jR6L9dwrXő݉=faTQ &Ǡ?H- :Y5FW}̋ۤ_V'+=~|?aU-7WQ'fЮN.5meQ]CLIr]CQ!tADJ">{ZS]I3>ɽZeH`L`!.>`M(DQo+RLǩZ,9y(vJ3:ؽS5|\fP:l#kO~M&s| y WtKE_O!YN͟Q?E2J&?eFf2%̓Z4?/Mrca)ucbȝgn$`{ /;D|f]O@"p _rHW]# Tް'6;!_͡} )^믭UKk_(:x츱J] $GWhrڿJy<jpVS&e;v(r|(ۂXsH̎nu_޽}mHS7j: v+2;V'>^ Kk})ݴķEI򱇠 w(]Qz{n01KM~KUO..:0 .wx$e!q׆) kkiGCDXCW0zɵ7?CF,IaŷZ;P# yzWl%:܇FK#ӤG+=ˎta* \A10ެ%=/xٍv" {آP)n{= WR[6wHI0 _o1bEH%8zQ}R,[DZͭ'Dڍ&a?T'#|E"JnUl! |CwʫJV$p\!\Qǭ(LQɲ;tQԞmiGq?]*sYT$\T#>b[c1GLewFEP g?Ag( Q8Gۮ}oq̐;:Sw>ò#r5 "uK`& {TG9e!Cv+ Cq9Fz#?v{0TamPS)FLpG] 7ц.—寧. ۋӷ8 . ~q|K{ k֋V ';8(;cyOJ ȕi"AϯBR" ]FjG֕XgcC'1Ž~p,lXhͦ@!Y7`^}0_ڵ``)W*bqWWx~DBB43P_\(ޑ'rJen~>65>Wc՘4 #)+ _$Hx)O % :@ x$}r<4ՖZ.\ìkuhx'23 fiǷk*E6WH>0܅I4-I8$OD&'OӛiCV`碆:LgUeעg+Sl:yg_l٘L^የyrhF[=L7L'Isw `~s >Oo+ 7Ewyeo@Sb|WDc7gex}竳D]%=t+>]%sdS<|p/sp߳Ȧ&~cAwjy_[l7dU_m!䥘o O)[= i9I7A@B}W)A˱:" ;6@21st\~‡IUúa) 3%e/K:` C`y9 $&صi6?S5'=YB8&D7:+e#>䨯|\4_߮u-Zk?ے,d[6 x{[G8mc,vn&xp I3X@d ~?[T$d n]u_>{v_Hd bvCmWX6cjT+g\Lg ꍆj,+XuSd<}:+hp:JϩWi=8nki7*eE>^IjP; ˬ[g'i=`w%Q f QgY,?Vva^13FUNI[͓iyGz/? 睤S nk:@ uɥ g: WSq{}mW$X?@:~\6kzށۣ,'Q#P%n3Hh9I݇gw[@k 7:W}S鉫k xUU yXzooc:c%u)}觴NnD^qr״RTw~}T޸; $4 ;[8mxNw<0/l4$@%'ݬ ; ݿv:W?9O/&yafOsMM zz8;xŇCBUˇݽ-Z^t%[ۭ/vCm[򼾶h[{ߓqѰFgNaG.Z?G;{y}d_Sٺ쎽~ڛxnΣ[۲pn f^+[X\yrbXPuC7r.D h0;v{lU0m7iKQӲW/v#%?z{.{â }4cyH{=+ǭSo}j!G^-8|;SgyYi?4[Z‡ˇkmɪav:AJؿU@r:T >3݂p\W0,1\  Nua>K'P&=]W# 1| ϤGO1*[!ֻM<~䤩N0d=wn^ßd CwPΛ?jܼ=ĠS} :vq2&d`ejcnAO mZ\?LHP'9# IN oiD]]Z􁚠X\ߡpDRx2O3: m@k4$n-6F†BfAQ⑯ф},+2;HWW:G1S;5Oxwj F@G*ڃoU]r3Z%1\݁EȦaGֽO,A[(nI_%G[GG_5.b&2sudZ; [>oKU3rY>N&(:O-'f pLM3l 0L-ߘÖDy+ա֠U3Ҳi+z]I̻&슠[5Э' F?ٽNf Ï,v'?jdm|*b)i @~']`;z[fuw*6[ ] $|r`w^O \>?ϳBc#fFiONYqЫh8 aBdA9LUs@ BݪBŒU1q{&e!${@=^ak K~Z00wd41>M3]=b\.l𑵾0$_H} 6ơ n􎓩yJO$> &p.:Z$J}B 8:wTLJ[D.κ_@:_Uoa1E00Av>]=H C$ eY@UۥVŁP/j[xqv>thʩfڝ,".k4ꊤ ohQ:nOS]TBg-4\ʇ-fq]084 h) c<- H&eŽH I6lo8=|1`"kii*/j0A Ndīb/ });)P(Rh4Km뷇Wu<%BsS9ynek<{иo^֯4!G~{,%&S>n!}ExnN! mSu,OH#?=r0 3%p[mV-9~N:x^aR'qsjcbn#56G_.}ןM=1nr`8f>4UAYxP +F:IwKkˣb_bB r嗟r5KJ]K(%u:x0:(;`_{wSl):6 'i!s?nK5IV?fQ=#u ǭn Z>ݝH*qKLIdNM% jTZHw`'D60kψٸ5)LȔ L  TlN ^Ysja('A3sr]``BH.0 X*FdEN\i1s[9qo:"vʂELb_D<Wd7]YHqR)0 &><藊^*~Lb >A^=A~9Q'6>-*h6I8aB4) 'ţ3NB`N1Ԍ*GkAy\\_JgYI=#% Lӵ+ Ս|?fXV0ГL-qC$0  =2pƋzԙEWw־dJ}dYJoj:>EyYMi" *Q~SZMDy~uDA `2NƮ[FcOVg8SJ0‰Q4iAwr\#NjR/9` a9Jx~pRMX+w8BV-e*~ T 閖Uٌƚ 3v]CqzN iG} x{8 a%\IDVvwp n0a19h)H!,p)o 8$6%L FU0rҶfdD dpr+0U vF:@ZO%!.!AźN,-H39QP);&3v;ALDž,.3Ԍ$}5O/J@]ndEc(9)&CXjsF,{/WD_%DMʢE˘X)њLeY7/ ̾ @Iqʴ5r$p3f:G\IkeAw!{Nkc2 3?h3UoW.#n!oid`j4vKlCߺ.i|ך򘃹s\խRYvH֋5G?*kM}m!-*vP4xv!/K(Za -YVk?__+V˳10kjR]̡1U]vٲgĭ8Y{*ăMɌ2:@Eu5:u#0Vf"NS^L;ށ1z3&P; .LFͰ9]!u?&#'}vZb؈_ Vʼ$T:fwP>J p?dWDӺs:D(ӌYM Dࣱ5{!?dZ% ޠy%3:G[?Ia^+s>O1ե])6"Mt5\ z +TжcFIP^g ,,EX~Fjjspp=\<İ_ X~S阾覀vD4ڬsehz'Mnj=J U<^Ht<}10>n.%ǢH7}4X].Z*l<=;F-Wy\lx#8ym ''yn]̾w_b\$!p6b&R{%Oa&r('- ܿUmx+xiWgW2\`FeK>ƫlJ0q,I9φu+w˯ }jR_jFIoxO2RkF橜X m>pnt?MtbW'+{u(w=3#⅋jt-_7 *`;qez"m{ia}H@5Uhfj1N7f&ka5|~U8H[qJ0g%uAͣ,b\hז;ԀF:,\hvJ㩃RNˎЊpжf敌yZ9l#o|>m/zuz# Ѽ'ol8 ;8:Q}s_nd>CHb?܊x&eovwWxBxg;_=1y٧٨w6jka|uiPBEIt_vsϵeLo$6 6&5:7DEonWV0αSqyvo4d^5e|̒eP6&Rc{X1X[M8W_bOe;[hnQf*Gi֟aްgi x U!:oj0͵W`YΚKͥqs\47"a*״fzmIȽ%<=q卆%P2 8)zf9[p `LzٶR}tY8b~%@\(Eׇ "ֽ(rqF|bt1ngV<ڛݣg{vʗ6(4u(Ӌ[ɳs,DȈ0c .̬u1Ȳk Unr_oӐ53y FE 2 O!mhu&3Tz l ǘ6Al (,P] _Ӏ2>ƦBƷ@b<p*'Kوԉ %ˀP8rnvJ{"](F9A,i\7=b6NrWjrNm-Oegp񃟜5$!4O*͑ vxx4(Q8 W"͔@R#eA-msXce*;GuH B!OC{RPM? ̱lܧ<1O9-DIWL[:UeWkw)`)wR5 .Y(.\H .?]]c> ͩo4: i[',bJ@{eЭhc4;.9Y بX`^A= (`P]ĸapę$4fElf. 4:46/kkVۋx)(#?Zgm(u*v 0 VkxmjxbcLh t+xoFg{9Wm>a=uz|uuUpF-qJ5UP++#ЈT_*oϟ% WZςnⲈe˚\C3A}.w<<%[5ޡlG;R1OB0, Vwzv8?u0~'2ߙyW;nL`O!-+ C{|!Me7c>؈k ö{k׈V@F+Xnu\#eֲ& iOY[KP*Pc91gcv hoQ.JwLRgeAˬJh܆ąIo{%=m;z˽oIp6ā4'X#1nCM784HՍr$ 4 m"R@ C2)ոhV(8Z=?p;2M+e:0g(3{DYlc|Gth0r{N[ 'mA۲f`T}r݉C34 6T 9By4yѫmT^(sS;ssñߞVmHG˃ViVu7{jY녜;U :WU;\!L6-_P=ČUl&R[Z3ͭVEC7e·7ߠ*<1J4/'~qNե6 \5/}eAs}Z#)Kg2o62y-s*[ ͩO1Ǜӫ5.PJ\̸~PwOeѝ*#٤_nA<5.FE) 9zZwu~ ,oa'g[C7b7gKyARy=W_w^&w`yz5t aj{Ѯ9x`[tB[mc;:RC`47nd6&񢗎MAẼg#mP~}JjYkgWy Ŋ?zXG欍EAr̴%uY.;}(žms]S-HiـMj&C;'~[l< ^+I׏wvSɘp{{iLP15LGWx[tq(zQY 9!/)fhL6gqԲ#^Dv9:XG/N06H ȭ`}k G= 8͑f\ C= zǘ9ۄma6ܝI]m<5ȶMQv%NgaƢcdl-鮩)0t\~:A<*?5LEEsxtac#F}^kw_ϦZW/V.,)U0/wrs?mLQX\_a0ء] |ZQoq^}.]//MXW" r7pK5aH F[R4$E07`<\ Kv׷CxIrNǴ5z= 6CjK9^֯։HOG+c c(1x+uAA/]X$7ìcUv×Se}qi{ddžE1&W/NqǦH\+s ֏F1cvxDd?oyaтY^YMT ƒB589 `*@EZYנ9%FX\sr+l]"gGOޝ1(L!+nA\J\\QߓE<7>td0Z;8I&;x$EEUNjoѮQT~-Mss6ƣǂ*0Qv\ ä́?U3r-nㅐxB%\Қa""~(a澹W)K/oҥVX&7N.zMo X.7{:DJ$3"Q\[IW`K5@:$Y*WIeHc94Č͍Lsd(z9JI؅?8-u$5 Q5[ĝ;_ٛG7G74hWMM? >CkHM9=jϻqgu6ɺG?ZRs͉7?s3g[F>&fu؊y*DJ*26 6%z$HZg桟Tw"s*QZyY A'Ȱs4vov3 OJl6])S7K㣔(6>^jZHAS Of]+&J{L >űgHt ҵi$R[X::$QS7iL&/G74hײ$5մޗVд)'q9 m Be3-H)Dkft=bIM  cS5D%ab}Y1 ot Yf 3ֺ6/ TfZŻ:w*Z9 j7vYSasW8cS+ 4DNd/{>#q~ϾڤwO1do?5 Όt^:D1tD b|km Tŧ'xr}Jf_JWx}ڢaS|ÀRW%*Γd*P@t;oA5I\rϐ@cJڥ, YKn=Ojm>?G8vٌcyx*"3 |W{78`v0_cVgCx@ ƙy{SOSgi y TͶn~̥Ε/;ϙ]q::%,@vz~ 9eh(g&͛xp֫S{#_6""@UdJghZE.i<.aR^%%*DB3BK=(_uS`Q%fcE׆ryyGs вy =]%{Y6=~ݝv,qt jtOfZs6Y0ӻ,dv_NR{-.qkTb+Jր:3ʤ0< n ݜwp 6)@V쒃P*ƖKt4s76tbӝZC3BUDt^2[)T;:Brz{utpA a{A_igz*}v<;Qܲϔ&`4Ne]&([ N*pI?U+=Q1f1gBڳL8I-m e F )(FƍYfa"n=ˍ,ֻ'G7ce٥~]|e/Uigꖈ5$-mI*/:zC hd؈U&2~^FY%9i$=k5&n̜%X"27t=~s6Q:Up505:C~SYw+!_E)nf[;[[LC`Y$:>xDd^I>)~+_ 0q}J]%0ؤL|˙29Gz$^FuDt. }1Z cM_xon-zd 7IT~v+=q71\RC|6?~ r} f7B`A@8A{8.Έ KmWv,k?'zYSR-!>~w^AK?6jn~Y+x]'b&WXэ;.rTLIu]jS=DIsX~ 3$!/JfK8yY&qP3KP3;_eՑpy͂7HJ"Hƾ)ؙE" `n|lHal4"Klw0*(^ ׺=ޖKk TA.#+b@W^=/ V2͛r9ZmM*¨@ۖ:^NZV4³YQ ^\0?>W#DmsӜ$h|4Ƃ'nڞrheyOw5,v}I5@>lzLa 2ſwU-yQ4ӵA" ˉ2f5__Mbq[oGJ>6Ȳ8jQ^r~g)[HЊiĤpQ_"ۏC1 ^ơ9zt4ȮU+J :+~{@{)ڄ!4#h YWxm]"-mk3泈@jQ``K' rgf㵻9拎;eFlwL>;J 'mz//DJtF94;yqfc3I{4>`0GWAŻ  z^UIvq8Y0\w"d♦7upwk/LT >3BDa7vW[s ]nH/%]B'm';فfPgXTeŝߑr5[?w\sX`&=(ߚ[:&l"?CfۄGS&a˫#(Mdr1>=}3 4S*OXM'yQ,ԝ ʝíQ 0])b!ZEe6qRlimb-"nt1bԣ Fٔ\O2H^]'&=&j0R!U۰wx#?מC*+r(2ݿG1QgXͮ!/*YZqTΙVjX(YWFٴho`T(14[BZgo|V@zտ `s) yPTZ6Ijzjrw^D_ }u}A!7B*n.ͣF1c0 iKÐV9zDɣbFZ\5Aكh~ǹK O,f5yo ҺG]]/?flj^ (MPVEw4sw+V/A)5\`igkѻ_w_cn0u@Mh ?9^;U6W(c3S4 lHu|~ppyojoS9{5cEm04,ߖUbAI"JUg%nޚmMJf l(7:-F҇]zyk{ǾY0c t"|Qk˳YGfիeJ.hI.+5|=~v2e/Y\:봳?Xlh?X0*]BdryNC$ QW'fJbn#aIŮ" ]"G/a9|S-JG<{J6fjz%+,~9gDHm[<1k#ZdL=)?$)qxhڅ rI`7*+LFb慭=ǥ@٠Y\soұsb]oCι#]-[R<[R 1"pd[k1})z[I+م0 YZ}C79}%҃-RgUQDJF9=3Bf edx '*=C# mtѶvᦶsc6HB?%vwߴ^Io?>mExE{DQ8VtoxN؎.u"KA▉iYI'3x>ɷ?6[4G0RCqCB{Cӭ(:zYT7qw#.cuLZ*Ye`eQ-TvQ6YiQߺ5nzf{fjѯƝn]ZӾ8zYDPpòQ62n٘;ؤb4oѯS-@[,Ce͚I?f>P 6a@Vj[~?tIr0!CY'}H~7x6j% =x^7VK1sOuJN\ū"̴N %Dž˒GUeNv)띖Z^i)=I~88N^mj|#<܋A||چDa+rǬܲ{ (-~$N`׫"t TiFdhRtDd`Hb-hMN^wV,zXB0D5,\+d1 }-ZT1ְSml%Yڢ*meVvMq ]V@PL9|ēohy"zA 13,Qt #Ł.*:Dg_ GB1g7˗NiNLr7_q'D 枆MV"lJDX+`&u%}t?5,1.ہlw'J֐fr.U@xguG0w[,cf4_Qj" FnPdW}} M@a&2=]t{=$+cWYq a|&4n`g4(`~hy@%^Їj9:F 9͞LPa,ߟ˲59E`?~W]Uɷ,=ìGdy$+iJr71un|c"t~S$>d"ms7' 58Z,9gM xGNd2FaY׬Y}JN L2󾚇(r|a`@0!BW:L8)X%P`2nH sձ ]@ Ii]ug!0-وm@t#AVsy-=4- IkW#*|0C@q;joJGxh093?gDN+BDZ/0iLctEOdh@fPŘ#ɎYE #MhoErdhΓ/I }QJ5|i$ ?y0חٲtYې)˖1R5_~&>0dNQʨ>jun:4Q\/-,GnyM!+S' kʹߏnɼ(Ⰶ}t|*:<[Hu2NS u)@A|q(_mhq\,dkNYM3԰HbCQ^˿,0ėG\ h*LgۺAI@kղBuGfw 8zWc/nR˲a]; A^cSdV5b/xI.INk(\16:3LBE*ാw"i Pt"U{WFoiispd+MV܉8ll quњizC:=]^a:}0F))zS]]6 "?;ט>}@8]qt/Q FiR˺2E-'Þx{X]dywm#zRO5\E)/1ިw!Ln}]؀X9͢XȻkh)x;͞8U!X]e,5mañg2c怼y Ӏw%o6 .r@ą1N{chl8o$]`O~6Lɀ iEyC}>ۮ3&܄[wY_VfX`!- dX<-h7.iYJ 3+DL+"sLj˂oZm`F@1CMwkօ̢u!/ ;KqSe C$HqZw`#Nk۫6 /W i`{bnj-Q97R'E8-t|%n]b1]DUQõ*:LDŮ=c(Sw3l8Õʅ\wī2AǬSKposb.Tht %`DN>ڐ aazBG2^prà.U6b4/X/2fRf%D'ZE(Y0h]ǭ2{Һ6YcB<{a :$4x(T}"gۯ<%w. Qz&O'~I iEps_noo-+B^xd=(ipIPh-F 9:`Jη3\RFJʗoUhBB~02\LKjO5w+MkdP_GwKbAe3wKbFTx-OjGcxn~/XZ'cܧ.#sv4>×>^/?/גnAaٰG:A5=CJD]g1*0 X]ZZ$ZO\Vꔗ_'NuTi3&4Bv dJx+dkxMkcVQC]Lkݮ=inKZ-Z-2lm%ov[dbf7[aw1[g X /5T}m' {DGA*DTgui<.UN ܟtxSk%vC]CY$\}i17dn ~B=|OtmQa*qx.Ϩ8fZ';I'=<@&}}^"ས7~Vc, 2S IARlD\|07+7.Bj6 Ao׾~G:?RzE9f䚕a3 ɛw!FvjgXNALk2JnL7H=8=ٟ-3 nF4)lJx7. 0 <&Uwvc-Xx>;ulQ2"&9 7xTY:v"KqJȝd3^^%(̠(3`m|C9G5jedy Ԗu>k2-@F&F iX eZ;,[! ju[2A %OZޜ|(f-?EE_\ >vUuaw0~m#UdR@1Y[Z\[4I-Xb8;(Bꢖ8 ti=zwna7{vb-WͅH"5l91X=}~WN8t0ѡV*fdUoxvr+ƒ yp*PhG" ưD?P(Ygz(˺lRe:Y0d]y]W8??) L,k+= E>:}KMUax$j쵞3hZMU%g.HA&y)t%5ʡ/UçᨧUW #}<x1zI C*c3>Jlu=YL` XI2r&֣bCz9yHH:`}}ezi+ܦ׾pw[Ga.Yk}۷T_?D8 ۭ H9J~4m @o+^1D:ʀ<;F@EB ),UdY9l" u2i4!Iͬ:6hXP<7r}f%pi';kk<y[vMB4&0gP3Z(" 7[XAnt $5 V 3 MTè@Tqx,vThuˆ)!R7Rh nHMm* yS}QB|kZckg4^TR1,wD{R>//|дU䵩VsS*b7:_g[GYg#9(^rεYR:# mO׿%aqnWwo]ZBj\\ChqÄZ-e %12BJyh,5-rp+62k% ɏ#Q*؞st^3hۡ}ي`\M +J >rTԞ3(h뭺r("h-؁v?\a&͐β6T,Jxp9)D{EP&f8?G ь&2{k朑Ѻf6s~q{e㦽NIl-iv^!~1%b3w5X^*)'C[-[%Y;ZM׼jSFJFIp)VI%<  \-D{U_&ke;&q!⫹ %1_|-W)-\6x.'pVuz݃utpj0zÜ>$=!dȁWV{&'Ojȝj]pp2!gu\/-ȱf YS~D$VGy?B+Bf"("KȆh4>te-a+pasJݚ3$Z28b&b@6oɦ9̿idH^4/\k\6Rgn^I젎loHRl5w4׆k{77\5Хh''U;bCΞw/s|nm[sLz[J@y;O{hT DWtټqnvrncVT|<;wkz:YC.+ǀ|WYEEu6"Ot|Q=km;Eɮ`< J]sVCkhMOq^ `()qsXM(Ȥ zSEoS/f ^A<6g Ng({?:_*b:1kbdMr{+gi?myiZVjY5u* >OXlQH%ce2\YHp^ۉ!k2Wh8L-AMWy d ^?n$B̞{sX O1 o $dqRD{T)B'+7CyKg9k;.H0L~~:/J% " L`/ /w:([cx?6zRj%j_^XMʼn=dS]?oEq&̙lͲIRi oOrP8r#FQD~vic $/Ft!Q˝BG\kJ]籗kgQ%1} Dh4Pfq*B1D ٌo,+{zijS+D~B+32' y )6J )l6 ,nn$^ bRtsӊ͊ ]^O\}sL#JM_(FzŲTJ-M 2Ӣ/ AM h#uՆЩPD:3"N1⛷wGFHv¼\ū56$Ⴏ PSdJ%Rx0aTcM"캂wDANHM)tݜ@~Z`xW'x(%&A˃Au4](X^>^RF,׌>y{s8yԣnF\:@-8px졥 瓜.~1ӈ<"1[QHՒ@e RC_mpVCL<" cOg&Y~ܚ nzNTLwiΜ:oY"fA4d "2ˀNCX2A\@KE5Q^N$qVZ Za֌j fj0Nj(!3 G@hoԦlyîYVNQ"Qus2 `)V*Jud9an;:bmjI'dus5"bvJOq mN,(u\@Ibwyp_ dzd[ QCA=/@4\s.k gsw)=mG[_1˝oW])4<__^kMK*(o8jZq9u,ؙ}6Vxj}f Ykc{`f;gcp\1!?or2r[=HW:Hd+Z'E19'X9=km4="(;;GjyO g^%'UOa,=Oo׿]",t]~djznr,^ б nxj. mIqpv41a!,J.x2)4(ZpMmaqHMk֏BFgEz9( žs<^>%u'0J ݻCQlͤs ŒG++'GT eyz]b!H,˔\{5 ,IM[3~=b8Qں9#@HAmTou:fyp2!'Me>y1v)XcTydžL֔tw`b5p TǹJ G q-]SnYΧBYauKOWE-6,w-flP-\dh%7#M>XP9ݽBx0H^̒ nCZrZf;e%,փqɷTyv13 % J A%p1/O^cl6PbM,N8gCכ} >Ep&fiImV!,w~Q$Y6JSQV3LNC:\֮HAK:J[EBIR]n(V!:x.4CD sҮO-_GkC[]F-vnBߕĂxb-|&^LMXSMUVo/,Q(Ʈ0 e۹oP$\Kݽʂ4gpzƋbx'05 x3~Nڧ \CXA.wrUf;y:/]߮r f?U^L0=Dҏ4awwV1zx57:F ^}J53^!r΅='ҶYqɡRDϩS=~f뎐3Vo8 $hiYm}C@Tvs:L *dz5je#r^g)WGH*2-s4(AGMC#Yя\'w =MJbksD+էmL C)=LuH4qo_ش𥺏fEOtg2+O҉)ބ pŢMBX nLq(Osnݘz]Řٕ3IjDDiif?iW_ZO4wXgH׫F>f3?#Gd]UlzjҼ7.]! l-hΖ] 5S:h<b4DHw k V4=z0w1}D/)})S3,/a9m,L{^ xg(0 sxuv4zn 0!lNCPbk; V5GN]w+JKSx.'t<q)׼7JΗp6v v"T9) e ju c˱V{w*u+EaM&T[H7 dcZ]1. wGs& x41M}Yronoeb7PL3Lrv)`z~6: ' F݉٠;6nW%Dpܰ@Q'C+;6R䭮k|%pÞ~;{Y5{}g~h)˱z]8e.gU`0Q__r"?ؑ5w NN_IؘVb0Kܧ4 hOpF 3]SvQ:6MWtyX'ȩ;w/rLA4<7ɘ ɖ1q)۲̎6S_>%&Έ=I`8bdkx *Fe[;$<Ʌ6col֚nBAz+1 9[YĦզv$<ɳMp`k'H"6ss \gʾ'wT i׉mkWȺa4i{lKLK )Xπgu"S:4+g=76i`ָϰV$()ffarJ% f\,+',V21LBu+bT`zEѯ lDmm `S5:0aֳ֞a'ag$-,a织G:ϻjlZ'"?oBZ3sF^31JqYz_&629k=`suCiXh)JC1-蒦Y I |MV^O⽃U *_ql[M:/4\GwVYpUr"~2jB n6 E3s%uͶWox8VL9?78a'^yUW̴P_4lkǣW[-iZ^[Q0rP;S*WhQ`UVUY^e^*?k`_Ae߯_MC;?_nnΞxmxԞZ! fLRä٧m8$|HO;S;-TNUaܓcّea$I/͍&_cA^nc1bA59mjSIi8\7}W&KӊAhG|ޭ4|3̗S=D(B=i6^l=s}9/<9 b}u35_T6<;9e7ϥ׼maٮgKwEo1_)N01gLKط4Bb Z!_ b a퐃ӴDR?abx(X@DB5> hQ$.aeDi}/yU~>Ůrluñ>cqKIPrg׻Ư >J /g Pv`3gwY F,ˋbH8aۦn3̅5lzKLJy:sQLK֑j^2 .fL 쳸) &xUPh/~bX: Ɗ9dt~]ef0Мd]F&+BmW@YL;ĒjQiU<%qgR |is<]j(v.w: YD8:*0 Ι/o؊z_㋶+)[2phf\ fdKnҀ_]#Pu0:5f276e^woݬyE-`cl+~:CKMuQ0Ո2k U L +[3\.횆-1_i @>1iry v4V "n?[6'B9Ng}goǿ&/34rz!(5Paul?/c9MjnΦ ckGe!)T,v>'׃1 ̃^]] +;#Zbє bMS#4u-b(}Gݶ:_&b]i^QJ>!4JYrX_׀Lto8&H !wĢGAZ#To+֔_~99HM[sQcAgd eLr p cﳿ^sM{W׾0dk#xӦkY+Y6׶Y|` j30&o74֒' FM%׳㙮 = OtR н%f`}58\ӹgq9(g;$fl)׫B_lehNH246yyFdm Rbpy6N[[ڔ9&5N* &NNܶ4L}! oe,0>̕92"`9r"񑟚ijHĴ ^09zfZ1W߽$q4j[ Sm[2?<\-o3dzm1ݮ3fpҊ[LUDmdZZa+y~) -J^AbfPj{7#fy< M4,QűRC(CVE5A h'q)0ߜ_>K©S'!RTlӦ|Z2j+(UM0`yNl ؘY[Xd%tztUh5d[%%(Coˡgѵ* ۲֤cg%o`o7IjZ| }t}+5 ]rFK/Ib&D*Ϳѓ_N9WtdXJr729%r,d ]YU[3Кs.;viQe2]&>0w<-Q^4yy4gZLɩMY`_껸vR e 2;RT+=,uEjKmX%!%!Kh* ǩ3 <ݔeկP'M׷aq727#$GӐ3Ř0*|1 F^xWS%^Eɟ}sIQHPLTȊ fbtD~CpoT:X5ݴ%X~6 KWyt}2T@ |R6R{bVpH #/մe\L-O4M$Shlq ˕U%l,^Nfs}վ'Ocl4wf:Ko_]3 ڽ1 ڍnt +: F^Q@Ưao1TE7|wAdd1ze1~vcg^$lAuUӏy:Qֵ&Yy]ؚk!Æ ɹA)Wm3kS##9>(?xa9,;>zߑ7cCKdg[Kݰ8[+A) Jji5w3ks1_mn|},bFX֏/ucWM2&ch |;Kfh-Ht{n W   %#OyS(MbbUZ -yH 1Hji5ѨD\ME3@rm56zk]}a\ = zG `#|Iê YPMn]籲H]H_2Ŀ|1e䦔f 6jP2x<̊t6r* /DF$Bn]uf[⛯6wvUbu;dIsS_-PÅegz*f/[m({)W\FQXi-睢Ub%{G&ciωҸ#Ɯ^ LT[)x'd ؐ]dvyuGsp0Z96]9]\X^W[b![z07ȡL2yJ[&/[?Qw6fk<UEՓۭ~LaBҨ"8Q0oAɦ^Z=j/^k 6vGoJZLpį' "5l> ; Jr*:J+zb|$)}Y7SVau<+eEۛGʆ`~U/~LLt"g >S2j>w W`y 2+bt0rJW2R_h~1 <V}(Xf %(ta( mP[PEMe^׷m6!(SOX(V nSB]_^\\R~lv1L| i˵bgHT&S2*&=?*{sA2*Yk~ÏEDn4XKt;zhn($skAa{Kи@Gź:+UR[`0+n^qvA%Gl+qCQ0$'ۑ6~qz&[5ศĺ1AbeO,I BUӯ5dJL d,o2`ƔV@[P PY#:u'`sd2)ȩ͟Y]_LrqQUv٘ T{ {-Ah4j#TcaĐ:Kj,zX!%Q05tڿ=̮h,2_CM.S!SBw}ˀ6aZ:aY.vpWnrK.l#7(lAj5\I>?mw夈]:EL`4rt,GzؘdۼيF`%OPyT;Oqg3?l  [,?ƼVԭl$ݘ@Z|.EP5i.uVưͣhAHsg;8|ӍC W*XUQpsS$򔫶4#?-* qVۡ ZPcJP ܒL[:cEYz1G1>΋ /kWIxXq8̇f f*͎ZeIV^(k^fO .ǓN]Ɨ6pwZ3͙c:KӲw0!2A '{S[,Q("1bz馕tN*0 _W\irq1,dsV`i1IYHSRlneǶ c0[}'F3Y$:e9OP.+F8ɭ c@7bVi.1 bdg]Z`n1(suxRr~Nj.>p5Nž;~VL K ('˶3g˒ A ya4{VU J$0ە80傳;3 :H M%R5$ 1#d+ :)`pYچ'}bnݩIrJ f~VUlUQy,DP61נγ۠sݚmFTeR"櫶:DUn+#$R=&m7).wcM{6Y#|6oĬ b}F,! -I7 UgyZV0RIZYj~"e:eivtP~ *sbl1Q1raV"a@5XswʸM{;c}=(,?wҟ ^?7 5΄6 ֛qFb 6 $nhK+< >vdTΜ|~W_0~wB[y:w%6 0$ʴQY2#NoTjɀ*/SANZEYId犽;gs;d_:%yl>=sO)"XieKdV' 4 )-sD:ɞj7$G2kGIn ~/PodusC ۇk_|HRu:2q"\'(@PKR)}hш+43{@D9yډN +A"b a@kIUGTpicw4_t/H 䙚/m; VՑQ5$kv;nO*`/#q'BMSAh yv\xK@L! $4b+VZ|02ME=o;vWL`5J'OvDp+!@I( s}+0/_sd<U}|-"^:j7\ yAWk1i5{t;#au4Ҡ_?M\V[n ,֐h/4DI;b%/,WfTީtODM9$;1̤ɿq^($q^C7%3}4Tߊ;߻Lj$\7haᦿUe/.J<ʹ%0/EdG}(+iby^exfШvqZSw]< \N5L _1CK;/uzKq<<~mW0+*>.%nwTs O?a~63 $2EmV-$X2]Ji g*;,DZ>ג]g|%'%d>\E3[z+bXq"_tA '>. v&]R]$~\B$rHJt;;,YT,hRe yrit0 +2 h;wt)6T=`)4!b{Uo 8.wʹ̷"/ŠHL@ET& \D@0mRbʪs[%M]&TØjOg)%ؔ% @tҢwлD? Bd'ԢBd?( @u`|1lҁ 8&p8'BY<%C݊%xPZ GZSq}@H-x\,LܷiXdH".Bڭ ۇ;?5y`qc;aܷOLEXXه`-4p䧤wIHg#ԓ**CTѴ' otlh,\7->z5~jZ)у< ϲ1T8h7/Z'wK.%JEFCAݘ&H4$P(iz1\55]MI9>LNiiY~+x~˳9(L>,1,5x靪dXXYVLHvVFDg1ol0:R ˅hS$p%5 ) ^\+M"ګjZW\7=Th TRsHrx؅"-926N[L,Ѫ^8_:Xd@i΂usfYm幝@GUF[S9 :@̠[ƾ)P{\ice6T-GG^Wo6nhWBKq6 Qq=JV&eث.c*)l"mi2Ic NQNNNpi`V+X,8XCXEb:r>czH,#鳔Fս,z[.ӟkl/~ao89llnJ*,`N*軎}wDnԈ5dg/Q*0Ҫ׌p|M& !e0@kHhw|r`9J#Hzz,N28M>#LSJ̧݈Zх $-_@ދ߲ja\P!%1YvT(P[ÕAQsGP~ONVi7\7nVq6:t$+'Db8BcJY|e3Ulceeq~s9A] 0v>;\b!6F6\0]>KhL PbTlrjltҞ;fnú ):_>xL_hPLH7;8zow{?ʘtLg wSTe 0 ,;1T лo8ɣ)BkkdUōح3YMv@`g$ZQE]吁XfZEՏ5;oˊF 751T+O3b6Pk4O]9Ąp(#uj jX8P7$Wّ <=doi|:Cy(s"NN~;=ɓu=KyI!9ҙ^b;oC[fpuH6r9gbL31dFl&]$f2sͿl=Ę]/ެ^@/1Of *[J,ުFnJJ!^X}~]pUcz2I;z{p4jM<~sEc bCb|GNbh狼Xy, 4Sr#1?6Тt@ #RS 9Q/K#Q ^pHĭ꫒U?5q9n΃AgJ}gos> +;v S _=9xnE/Fon!Kp%ە L; ..x3Q TCO1rΓcb/_@_ $ܬ7n7HrW`埢ZR d ֬ J#2n3yEZf V:&~a^lS3(XwߛvUXzB=D~Xl9Q)Xl>S6Aɡ|Ъp]ruNr8F eEj4{|/yn 5LTe}Juo4zݳ՘% *5UÑ+S+tȁ9E,\%{);"dcP G'hA?-J~*F^sdP9_[(OK!?2ce?”n$C o~Mڎ7ܧ^-:[m$`}yG;o=^488EJ7b;9kJQ@_-C;Yt[XZoآZw$29{Gc@xjk =yg>lw2<8xQ+ ,I4c/TKVPDSWkBo`]<'YA٧/l)Ɣ$KG!t"*e=b)u`g~ŖlT} b8* |Q9-̘CS, Y4$c)*o |f Sq3 xVvL;5JTeZc4'87ܲ[NͲts`gec"Se&jg \1 S=z!ma,i}vYr'Xh^K6B ]R \aB[&MY}%7PLVQ"#PGhڬ  g=zhf=EYB$7lfC7g5I.)yͣ:LRWd`@ʰ5)-o*./rX5Zpp8ӎՍ Wi~{G9[K@L'NJek`/AHB,)hWVX)&@/LxȑF޸}h}궈ͶP24dYopV8<0l+#NG,I }Xo}F}-i&NH-c5/RpӶJa`K2r_3U,EFg] ΋GXO3/C \sgVYDk h\`sg. 'w[H9ۜ:pn7ƹvTqzTD?h~.0u+KEO@i*hP\k@byZ)s\JZ(65CAg;k^A'pSs.ZK(($]2]r@NsȵԼּM:QD„3#3`9բ]1ωds ؋9ޘ h}²E<>'{ Irt&Q+;O.Zɠ-T$ha^;Hu)ʛ-HR Tp4yg$9iE|n~vNd>gUzƨD0!M=r$V7$ɒG 60h~20 Yge:sA(r kJX.8UŦGCñ|=g~FgO'@Ȃv>*aF?u&*$jBClblwnpcvŌ(vʲNG:"Ѷb^k<ԩ6|ބ* l>%ME ?9HX9./ON ,apq(nvD78F s5j\K2!%ahm@5p8*39Zcsتİ2aƃ<7Ѓq#J/]zE}W0;oS8nc, R"8ς7^ݵt,NwB~J⾙aNV6ۧBWCO;SeP0i6N ?.Mnpj+"'AfR+Do9C&2YݡLNA~> 2BBkfônk?! R ӂHFK|ryS3 \XD4bv`W)R j/mKj):J"uUfՊڶkHX8u@JEG$Kb!aa^abN(I׻K RqByVH~9N s"b*I& 1%#'7k ZJ;[ǻ߾z8lO20l XĜ<0oLaEw$->.pvzhKgs v91E.>(f*YOc)i<ބIs5'ŒcC!d5 lA6Dp4[\*rC]gWq[# MY@|!~!Q*cB삾%GVGi̜[b]Nw%駶>``m.Dq~2۹szڇ^vLo%w'Q.uG֛ٯw2MJ5V&8+l`cWGtN2V?V5KdghȻd^ ' \tÄAoFPK#NB4YvCO;{2yV0j 1؆\%u+uLw)Q GI-j( Wd%焥B Jy^d WY`$|Nfny,U] 0qh &۹80񼑃1?llnimW?"\jjmq ْC@T}OJiPM>jXnWUL?({"I_CZ[l!6;:C~ 5Vw &|leP? 5fV ,x _oLkgl9m'uC0,Rtg! qh`ς*6y8e#֬VK\g kd:|_xa][ʞP^ c0 $'A傉'(8N2oԑy͜<|CjԗɊb7,'`N;jǥ`1h XQ52^e(c ^y@eB# decQƗ R>Llw{ R)P" !l+ !0aa:lDWAsm8e'Lv.=syFEChԥ'g:ǁELB^AyT3hBM{$Ah1R~-G%#5cWo\5Br6Qk F U,1YxJ7xWA/mXw AZ %D,ީ`^' >{?F6镹<540"<#fKWEU ȭ!o+mѩ`_Jˏ'>)D_@YAFy+(x [NXz T37Qxˠj3'\# dU:,$bk Md;ߪ]2W%(%GM݂m~wg˿krnl7w^;Fs.b:)JKd9o52'-/5'Vծ>!w59of)j9Tnt%KAA" >LJߥ=As𞈣XY}n~v_iU #4w؆Wt,u[=`.v=rA_!ɥf+lFA\vGUPqEǀvGp[.0Wd-^7IJ'P- &ߏn'[ usݟ˜}]!W~/H?4PR׺,Kp(Gt.0JǐB2'ZNIYYƼ5>#'ɾVRu9GF3!ύ)@{HSu+%~>@imH{B4\t//^w *=ccy;t, [F~Ȼ;;{wAu+IO[BwKs=~,wkTd >b΍FxFSypogAGL)pjXxx[0,Iu( ⪾$X˥|7lo*C.1b`MN7'&Q;bAY E 7E`)(TXR $+5٪[@;}|h]`@fgeS6)-s_0-YRX{zlR,`OLZsTkUhc7f lZ9^n5l.7oofX}nc)u+#,Vc|e&xUq^/r3Dʟ3Yx+.|s%S .vH k.?ѓƜ~ zryق W63n(K9d$[LL-˟VՓ@uX+w`!$|jI, H`;ꦙuؕexgax7o 5%(@y1i5^368^_/֠ybKM]}+s%.,*,ai%XdHMiGKk ['^w|-QIۑ`ng|.?XDsyA[L% :p[ķ==@5 _͉pG'Bk]pV9܇-$mohwY趈4D{A7LEBB>/Q|x#e>œ]ɜgKNB+&qnX F=r];i; yOYo&"0(Yŗ77{ %0>?-9uU9-F[# S#y֓Nl' ]H-s9~Œ[tGܱM76Z[Rj83{ \:b#'U5+͏WMG`NJT# rbdJ$s>61N`.^\ -ڑm"ϖq 7 P S*\[2O&36d2\& rPt}씧{pC5 %P9`A&>ʌ؆_s!)-'p0^봕o+T~]dP, P7ܶrǥ/jvoĂuCLʉ9`TUg[g_*5wqMӁzd=z~O R$h'5կ*`$`W0BŬyh aS9W·/!g$bu?]ZA 6$y(8jv@N_E)+Q%4݈-ҟN&mO=W*|hjKM*J:D);=qyJ+yWyR<՚+M=%njT}Ք^jX8Qטgz5]Ŭl[R#h^C.D_CH+y5|OdYX}!m:;8U94t:d KNBg>eҍ m{lX5'/逈Cv:p,Wlª绤X,XZYprB&됅w0 dt6)u78`VUߔ\DW*#*ҳcA!_FdʃʄPAúUGE(xnc WF?5DNޒ\zUͭO;{ 5\b٩̈́^^mR<醈XY<,xuv[=\ВGɐnUÇH.>^ ?cdւ*UӁkgp~`kݾ350/XR ku|D(O?"4Vds;Zź7iU2s#8>'}^ոf;n6l*@ceH^($j 6'-~ f>Sj'Nc%L;ܦ {O~1V3 NwoUZ5Lm2pw9tnn /# nrBefx#e큌8aDx^s D ɮxq1vz{4qL?r ;6zrGpxc([; rZ˾OqQjFWAdI{@lԛә)|nc!H1ptylIˤ-yd5`^7fyk*fa#ziif^SNs,yOHxj*6Mk);DV ΚcB6B%dtcw/u9Zn-Luv5۽VxtipZ}{r 2m"Lk7z!dz5EPY'⨬ۊ{3 I.Ci* v6ɂzZ}F=τ?a7 ʣO(SomZ8y`9syI Ȉ8 $f̊.KF3;ۅj$&*o$.e. ַ=)a\SmezVfukEUJɤZ&k>(E%}dܐfTD %@wKb`kd,jJ@X{ݵrocS+Y苉uY`41~liAU!# #j -ukF ,&MшHv=̩=‚Q.3%OCu7c&w-ٙ)mĞkkdF+ptf$%DuVJ3*bNktmj Z:v]!?Ƣ1HHK b30F烱xD:$w p4˓$E:K䐧#> 29@%g4M2`̇0@ؿe h+{Ff@2mcR -Qk[Sq^ pL!]X*qYRu+GS?K~m˒˽m! {md| kBakT0D v_ud!z֩d>4,:HArA(9"4`pHۀ/چ \ڻKJ$>5V;,C_83V&ע ~x9w:]*TbL'KR K,Q.-9 0Zr@\cOIm[Y ފhb%')ɲ6Tԛ1WD_x`ůwւ>Tׂ*ZLZ9Cϒ-(^uy+ilfVs)7Ol L6M%YMٕ%țIܶp+MAyU0Y ֈ[k'IAxɮ$˞Q˝gto ۹BU3f/?f\hk1 q Ƕ)]k1/qý${8Y(TCmfә9w~=KV hh)\]S9-C 5JXO8e`/`SG^EM}=<RXd52qKڀǂ//ֿi# ѝѠ#`FV*5u.[e|RpR-HTHѸ5NI"-'!"BdO#g-EUN,Vdohw6۴ܔCpKx=;_(iUz͠34r9}>v{'DVGBՉ;ʮ/.< ()&3?SZN#eG*~.XpaS?ZQio&[%sFSV9竛RbMƿ6ͭͭ;{?46ǎ_Iw}DÈM,4fhn>1yWXgFaVwHDN H\bE6Oclwn߻K!k,3,nFF|./G3;CUB;uY(FZY457H:bsser ,nbK+b W!%z74>E>l iSLQ-Σa6(m)`;'EBc/dp l]ef Ƶxa $Ô!֡N"@!w\ef.2d-PLVy+<]yZY6hw[_ί2U@ӠYQ/UٺK|/[,GG@ϠCγ*O5ķEϥIޢHeJ@̃)~mSnX*0Ϩ}i@D$ mo\>-,CywڵƒC,*Q@CU[]>98ߪxHzs{i:j&0DF7YyF_bP3(oc(Mՙq㐼`I(Z|zje|7}:0g)(e! Q.wsf?%pMyϲ*cQv_F#.t/X ay>~h2׀v&2b E̮wGkH]8wEp3(QB8.[j¡s|6^r&É{[Go >8 g^̑9k0rr ܢ\DG/̼ңa<2LkICB }:J8u b\(4o<ҽ#{ٞ sgQ^+x&@oAuuSP֓KuRqkt&7ޥQ$zdO[Խf[-V [X{<{ٽ_'{mFO9By^tۂF1+y"f^Vfkf7Am:7Fr^}Qx{x栦wh1xH.9Kܯգu5I2*ɽ{+{/EԝtfĬ-dL,;@P'ө萴4o9P{4YBUB+ E˝R$-r%5߄@3/*myuSFW;(i"yQ FqI,: &0(GeA¦aLZ&G,ޣ"ZFӡ3=yY!0Ae(%I&tW<; O*z8\jh- ZSKe֠dt~l%t4n&j.rh,䀭y]^$~ED̢ aPIURp"E ~H:C%r~AGVۑKI"H4%`ct6bS0L_"lzmp:YY^^&h~dhy"Sf5ibfygS\52-۸5Nq~pz 6R ìu!=: Kٙ`jGSiW$'L$~2YOPqQ|eئdMZ4' w@*zC7uQ3%M‹AU%S=iq;҇D#?P*4;s~3()V"wđbAFt`鰍a q j!J#+i#F,r! Y?^va"Y&ʒt4H.>g54TGm$-^ir! 0*`=W=C?'yƛCʽN[%{oP1Bzd1ɇtO{aG~L6Von`s޿ޔjsghU9?X"P>"CGjvh4$ag[wD 5h@K0|'3h$_G I*# H-ȃ)fKKK <@$t`n#wh#^ClgDKgŠ!VQF'F w{2bC @VЍc3D]ŽRpdfk凕է&/cb'wPy[@-~Em~MXq:~`8WZ!%-YL-z!˶igk-5 gqWszZ<{5w`m5sB|ӣJ-MjUZy{QVR/;:l'eFVbZiov7X d HpE7UbV_|S^AEu63,;M;$cF2K˦C 0"aG;%; NZ'1MZYNKes-t=Eq&&*Lb̽\&@f&LX3\sSƑlBvD#HfE;zhoY"xK.tsi"4S0:LԪ? g )IwOiR, y^swA+Rj̿t ::pi]B:EǯKrjxsQ݊0ejW|/@gO Vߡ!9K9;_Q~ISJG+Ğ[2oH[\5t-E#WQ*^.|%vYtPaS`vǽDUǃ)Gzry 㤨~vBR\<9rq YI8NPM򃙖_쁓>lúkk[5n-Cn*D%AeXֽ$&IC28t9+7hlߌʧ,Ž5{EG3D=uqr/VDao(7RM~;#VP~or:H_:M@Z2_U ?&-3JR!@VOOPY'؅}qd)Mñ{lPL)'LL|t7ǿ6vl\ ~IeMJ R& Kk4j]-8ks 1uoF.eu?|A+"Xv8uc0//x;=:Vm`l,rR7ج{+Hǣn";LQ1jb@ڃSJ-Oȷ`&p`ZLۨzw/)"BEOԹ9 Pew|iDSY5лG5Oh[G*l+KͿtVywPWL7 Zb^;ޤ.\n7~8nls #AG6ԷoVB3> Rmx(Z+Wo{0;8>x 45~9P-PGƂjHQݷ{*ORMI :TzD, rieAIK̭ P\-ȁ!DD,ۇH@JOMb02G/1X{ZNS[Y[Z?aL7BO>J<)jg4g گǯ-^p(}L= b,q_OMyT'(q=IoΣa&/>8-nt<3ۧgg8'+0'x>,&3Vw2}xzn Epb].N슴*tehsUO{; <7x :tG4b5g/9Ff*pu@5{ROMfrT1q(DR* CP>8Y"kɥTEcP=hdKBBx t!<=@(zrz$Z5WqDHe~zm(Ej<ũMI` .l5YBcC@} } sg w֫J] #.~$cDOV?ty@ge?VExxb;es>JRTKb.rp~uۭ*5JS?R3_SkT']G |oX?!&F} ,Q~zVsz;wnx(|$<vJYHY4OMgJ {(Jnm5zՌ^~J2,1'.y,Jd(Xe7"5ک^EM}gԔ)p43ɺ:E=Ea)1lQڛZ1 eD.L;:CjS)"&u$ShORYG%<~vth)w'8,M<iPӾ4W>3V du4?pUSe]J跛n_-^KΟn9_<4J l>gij>}/=}42\nu\gU[g}3n@r! fOWBDZ~jm6w04R<= f!uH D!^?@Rx2gN?i2B\4J&}Mb'ˤW:lB9%dl%V,p`UqMu^7RM=⠲3tc*1UmAEx.&]ȍ)&z(,4X+;=wX ga=|'^? Moa` (%'(NFVK):]3ܭ//J& %]oQN:iOB NS|R#moUx&t=T0ly֞M;ϼ;t;zj?B ,; RS):쑀guvt9vnzBZ-#\oYKPf%^z%((H9\%Y/_rGVOcWU )_遃6kq\'S_ii qv!龎nF@z"O F%7%jb<[oOu vz1h]d&НʹϳF/.s[3gqXEy.Ns$p#9rFgX8a D.:2UV_, 7?fWO%H}92+):Z(C={a՗AQ")iS"T/ ΅ToՕs5O4*J7dÔ^X6fv2 8fkʆToGp `{VVfd(3QZA;rERtEWdĿ~Q^ ef4e`3O024bp9gP0Z\; ym??HqJ{-'~G <:j(5\ddF- !pF 8:RN`_ 6N` 蒼#0@0>7F>ΤˉX8yr1lw& ? ~L{@,H;Lj0aS-*8a\M/B(/ҋt x *}0Y$scopB}dTŁ6+xB/u? Z{09dҭG?m4ݔǛI'([n.=YtIkS,X٩ة,ul1_Ε8$/;GOHSw8ʘ1xݭ.D|sz+DbAJP:*8Mw'@I.?R>rGh.) ٙ|`eonF+X\)]ilk+Mfsxdy#(%0|VMw[$0 0MrwN7f2&HHW[c'nrH)nAq*5omN$&PC6e=C5 94 Mt}뱈nG}4cW0[{CVv*?s5m#ә@t}D GNvt\\zY5]EЋ[~[z4L,BcA)F}I_AFl(:oDI=' ޤ "R(!`א 3E,^&j(cGQª9ۮ 9NH &vɄeǎjTߨ8:@vxCMar6 1P#\F!>9 $]S|KFg35+i6PZdcKiLGƒ1-!4]DWoPǨ)Hz;DǂH4JNPvlu?cCdL^5R&&ṳ&B!\)3;a"}9)BUPONv(8LqQO 6S\`ڎzoঢ়g>o?P5ŗ![8 l;:ćNsrpOr++gҝǨXr1PaO poĘH;y|vпN:$IQ:]L#yOPI#Zq nHU^2N'^)14p9"{V*u|Jʝ7gl9D^H!~z*a<g :-[]aG?!.|*Z/yM6qs´@v㯿O,~ޟ\pҔ!VB| Zon?-b!Yv!7}XLHa^+Ơ1j<;((nQ>D4ѵ-{g?L&JnVKm_ w r%LtVOzGLL)&VA%gγgk|Ii$+׬?1  Cv9^Cc΀rZQ[eRkڧτ gqP6E@E0OR/WfZam|6Tky-d5" yݿNCXȰ@"hfarQ{ؿuZUzB'_j/KYY" nA6ЅO_-Am8a8ݢ@.T`۸0xxAD%F4bN:ԝaҤyE]\#PĴ -GwtbK[$(&nŸA0& ]-do 4FD!W r=6J-,u`׀ { 5rK e#z@kG wsDmYԲC$#XpmEAC'-E1<EK4L}Ӂ׭ޮ+knRdEE1fcOj[/t9ּn^՟B(D5Yarw 0%:s?.^>ҡ/Xw<:#BtE ox[ BZJNy1Zl9h`|yLYsUjqnZoPU[l+,_qkwSB,NGiG'@J솚ѐ-8Dbij18T\'6ް'`Ap  YElr=C&e#χhm >3m 1:"Fj[Nίy0S*by'_6Fgv>#䄠yf (E4t<6RhR_gO@4{jk;Vzp 3fs;Jed7,^m4BqQ JlDZ^0~r3~%9;v#G'%GdF`SO'=^;@kgfMdls+@Vc0X4m2NUt@&Bւl3nA*FB2c-aEX|pͳTߥyͱp06Ήʛ&Vp&RBѾ vd&%^RxyP+kF3BE[\bV$(Pʽ4UbwElB-;ʡwK; pQ3lJn(F/xYPء{`"@˭V+՜-M].T8,HQU ,goϔ4?[j&l:CҪk~Nʞ=)?!>o(D/^`ɐ8;A: ]ܫPϩZ\€+e'֖t#:#bhe)>L+2Gtlp' /+_=liճf%0)F@6sj@}!_'xе/B4\GбWE ]Ft B҇\\MxQ/јvXJ/Sй Ĺ)#!Ӈh; #:(QurB%Yrk0KG3):ݩR@43oTt{O(mpAJ^CUDgP/M %`70"JHK2I"aUT3]7=M*.RJӁ^9N~U= *M"Q\u~)B4NR*o"mS煊usØo \RJ#[:MgO"j Vly9Q<ύ8Y]<.2g x:xXWUhGEEg<%b=ɘh?^LbGzlYe,Pr}Hq& )7M O8kw*GdA5iE%H{iJ 2ZisRxC#=<:Sd8RJ% B7DR|(,c\@5ӠFgÕwvZ\>hRB2!lALn3}t;‚RX@&g%\Y[}f\DҕVן@(h^3؃L̇21|H6fr[ɿ'<*%TR+'WHc­F}FG:a:AT o~V _Yu>$TXR}ZSWz@ג;"\^%Qm%2+%F+*V:G ]<cģmw 6e\,MtDh$e]U֘)[<P ws{xB<맧ϺdL9J0w YLPa)"!JQAu==6"jFJ!G.y ]cdA|q/տ8K s&6Hy0d8A"VlYM urH.2#5Dz[.eFT#fA:,Now*o1M'6M5bSYg# xq|ӓ0iO :;''kXfHfd 'vQ;&u61e p,x$ԓY­%da6LןXC?>w<B)K1˩Qj~}])6|b_>-xx?gb!P|(턴i+^(/̚bY/wޡ=WitcbvBdQ:q,.(g5Я۰zdS(Y#לcw]+ךxD& B+]&ӷi ?4X0~.,z['2FcڃQ՛J\o6Iqy'n6 "rLQVG6G5VN/^ci69Ic>wj;w%8=<؍w x4Cl/rр#c3 E7d|5FsC]DkMt"Pj;kA0+]~"LM)|d!#OD`{>[1Yi}!i-6ak#%^-L#Y $W T(ޡmp I>pj;8Kd{-[J[$'8H}$IaA&7%A*_FQ8{Qv)@?^ʠՂX ׳˯^-Bi]&I{Wj/IS_:l! SX$ȒZ8`0!2d+ynMXnNq3_ lSf6Ni0ozyOc(C MRXHFE4,ڦ,bQP`H9Kzacw؊1Mhئjl&Z g2Yn#kh] .ԍ\tp`" ٸLHyeoh]ؗO(=Q5x\mk2?O{G4i81A iGs`#P j){lY #wy70<|z4&?]*5\/,5T :On MQ7b1Z[{{ cdC^s@Pn2tfe0/a[5T9-@߅#k[KZFqTJ^Xa#Я8ޡHU,c3C 3o@tNvzjzDO/JO:N+*j+Ӓs3# X|&#TI7 Z$ZCӒ$n$TqxDSX1N<_H,Vjc"fا%]GU-_Z]]#3^(;[_Oq/CXjCv3c%. CuD '8x$*MdgvOnx3 +N͎%bi׋e"ZBe6>eBe5ABqcK^$$~HJ1*[efdrvmj$/42Þl0[sBzu&r-CG#AU Cē$o(J}+wMcIqziף-qIw0eQU sKb9f|}BlܒycL.Q+K/ D?G{kJ`Ti6.G6}.PY0*G?oS]ԔřH<b@jsEKAy;h޲ MF*tECt"nđED F&Ho!{;w9<^n Ko4—3! %!fNe6eby૰FUVgex"'Y$nl1f fǥDs5ƒYJJ.'98nW#El쩢tpio+0r{yyeCG prfrF! I RU1IDdB85(-70OMiw |6@$ϔaia\)Sڊ;Z37q&g:-ըL_3*q"Ws%^ `|3ԏ Uz5Jk`W2ISC#~pܫƦn-gʯUi,6lj/o^妷'ZD{*iF]ޑ_ o*O̸Wcy?Mڍ/ßO07sC!݊nWd_UZ-tcLe)y0oz OZwN;ɛ( T27ܯM$cKv^YQ Y{/멕Ҳ]FB&ŢM4Tl0@,ܐS 3'S01\+2-4i`EJtL9oTg=*brvݓ}8?=,Yf;@SEBBoTjbEyY+iÎ~jX߳e2r5-bT5í ZнjψJ׈ag¤ q0J\%>Et4ʒgx\/>L/dcĎFuYI'`fҝ'#Y {nd3,ǂ;A#(ZhOϓTGDTGP}7>RM~BLl-\ZϠ0{uָ8X?!gm,JZKVǞ=deD4.JGL 9lCDeG\^i+aoOidEnB.-\d:PyIś$ee @/67_gJ/ʛ%8ðY0;Ɗ _h,OlK=8_KVvŮKf]٠ jjgK^V1t mGu) ;PV oQ37,{{oQNrr#}ZIM:](^iHkc@9ছh,DUvrCm[Bi A]~N>fQGX /_}Šɰe6H-ۛn%H g[u3 #Ȓ6CJp Y:PBMbEy&I:9ze.da+,ǔ\?!Vl+d60';` MG!…5\ִT OFZRWP/kG˺fg:PcQ| lKL/. Q*$aV{M @ ,M\i b GӤ?VD!z)pKBph֗E_N%ťE.RtEC_ #04 p6E%ϕL-dR :Q2ZtZD)U |9 ?r,~vsx?pcE1[ wc!GD 8) h~*dDYcXE5W٥ op85ttbd/Z2VG "O4Aƚ 6U]GByke6^_w$IH,Wwעl;~.nQKV-nTXiR=wKc)WBZATfWaRD ~EAh;EI2rMK(ݨ͢%Xp&74؆~̉!D08>99=OO_`\a;Rp<\* s]1:>;Cq)Z:Kg4!fyr ٖ$샔gLe **Eu%D' wpTת}q# D< M?XRSA6д]3J>$]<<9z0:(պҺzǾ)mMBZh}_ޑ lٛ(Z9C?%t8pddcs.;|j߲35Y \-U}CbH~SɚsΩC2ܸdA̍ zUk7 80LocO[x&I~xAw@߾"B|FZ8 7biBgђ~;Vfko˜9pƃ5D]6l?Y\`vcvu#s^bYtG3HB I+n'{x^hضtZysy(. >U5aw솯i!$d CuΚ <z?!N (2QD1 av¨/nH4jEH켯q mM7eP˜e?'*׬^v]NhM'&iaFm:\Fs[% Io/qxoxgo C79t@ӝgqRotI9DZA-P/Y캅K&g6\ [k,e*+}pN.ŗv1sEyd6eDFeq_c͏T23hv ؏ja7X>stLVR B:`,k kS%(/7Xê`-#<\EVrʴqYjz-sKta5i~Dci7" ~BcYҒC[lb%GAx79յ:equwbJWCFژs2wEPXݑw vWvq]rK]ZQE,I YQ#ݥr"wLcnՐt\:YUPzrwV4Z  Z4QđgW $2_Б0jP`QIc# <6N e mCǔ+e7$Sb j_  P_%cwRR](sպTozmj*d?G)"4'R4g2 ,\իJF.gs޹! tw#9؋y;E7#6pz:h;>W0~(g`|;Mg ._' 2;. Ii~A_VZ:,3lW 8E 3Af^)BHg[B%%ó^|{}yMɈ^GF_;,t51 dw4m< |z0Waoʽk~ 6K0C/ʱ @Mq IZk46!*-Uk󚭖`\>_DmAi o5ӺM'W*ed|wqui4-/h][n whyHɪpO(py&9Iql(j`VF, >FޣO-=/ìA?`LGlp/NӃ렻ۦ, }A('Pnƻ%=| |QGQaXOQ9ZƒΓith+].qhY|pܢB2: E`G>CPmЖ&$ ډB^Izk!e|ᩀ)3i\&!T 8MD4jbIYjda. b{hZxx~*RٸևFݺ %wʹ4*(q:| ^FZ-jvt FzC~,mkHJB0&:}sq)KR҆AWfZp߽$bQzG0[cCևG-Q=7]7.A@8_ "@>(hfWÐEVYC#흒 5@7|| cQ;cPyPqإgn|^li~ u5gޅеd) >[oD/F қϩewMLaKH v]_%ﮔL+j)l·K³h^1$='6I-&Յ pFx&5}sClPP;w uD5e"閍JA͞-~n'}:Wtz|,*.dem?c|4IKEyFbuWSZv-.?n+%bX7 s/pԈ'Hͺmۆ5U7>c8;j+&UZKuVmn0%3>BxC a~q>cJz"{ɠkry"QknO:vmI\jFѬ _(^x_lC ByǣXk 0~.5ٸέ$s@a߭t)RE$حuLg ȏV JkxN=\+4Qf4ALquޤjC@T*E(W`bN><@h^Lyr<`% `IF!b`^%Pp5\*cfj]mE5HW`KXAɽ PJ `WBjlӥC.ɌEOA ZV2z (^`6X0lA+f?Pb>PbEpӘgup_2:} 6<pbE+.Eby} .0kя-Z..+g&g;]*W..$gINJo!OT2~,lBHv\Ɂ[:L %t6pFLJLU3Ycq@Ip8 8T<}uLaw,(lR%) GdqG%V .Mg!hu Q?i=CE$Ą1 ] 7!{JWn <) "oHe[ U&ٖ{ܤ6 5e{@U *uaD0jT!˫ *D-}ppp؂DKr e[ (4J>Dy +?R;kRHGC5*wnk`<.DۅcpB zc}dQ@?DH=Ԍ*>?jǟ5uŤ+鶭׋W_ Z,)NKq}=O-: BG!GJnu)X 0ͧ󫙔"MUW#vuї,}o7҉p»dB{Bn])G[J%w!JBUWrwj]~TNd_a[Y£:q"D. ɃGnlnnmփ[%iCpb\q렻( (-F }]>ºϛ`@vSD7 &R!%*<ԣ<| s>* %&JPlkأ? Gb0jDF\8jyN5$O_ ^iՃhv+,V^tHNI6Qv[B`NsW7`JAϙ\2Х[㙁s ׉ɻTm 0y7`jdxќ8SWӫ)Q18=хZpI1vv~iRSՆzRdPMJސo@ 8_\sP.̨EC?^9Dz֖Ӳs'Nⳬ'Y-!;5@iCPؐkG136) poӚE#UTCsH$g_/O;'?w~FG 3p/U// JbAU1>"c,bubDVAl VHVzѤh,LX[UX:T5D&Qo.?t4"a$y7a:%T=1W~IŖ6fl]]wtݵH湷QL.?9< 1i5і3?_ދIGR` >W,Z$4QVm1bAx߄MT B"q#s_ " 49>#fg٢Z[ FVQ=n&U!ʊ{EALV݋V]ښbM\\tz#S{H9M\n(\2z}SaR (P0k%ʄtC"YBB,߽phL`zJ >(.&!V*xpqaM@z4}b1KoAR-ZUEmgID{#{βlΦiÛu&(#٘ɓjCr#[F( !Y7V$N!AGA ,g~:_:AJӵ˒e֡ݞ wضb;:@<$Yl}{CLY =%;c j\f˫q\+Xu/M6Vq.IɦE~U,4n kNB4 ӒE "y&ixl(MJV'ƛe dgeVVهq2~x*ɳIa:^+M.JW?Txa1*o A_.W~x`zguu')LbW8c專K|Tl&a\תF~y9;|uvxjeMRģ".ǀZyGcqqUhB@'Ύuᆧp@Q~|xV%'g;y{8tI~L +1 V<0_7 ăMű>%xut 8?Ch}fMQ6BPDC?̿>0ؓ 8?Hx\M\2gXTl`8 6J4 a'qNdAK#h SVy`<Эd'_l8+J(cQViDzKx5xxSVqV߰rIǕIyj*G2 X>&Y\NADP,RiTr,1de̦a^w-P8 `9쑅tN̻jV6TLVPNj(#ʧ-0\lD7dQ Ҽ{AkSJRd9:()4,H! w;3lgYRE DqTu5}X?z\GE&a(#!:[ɐ)V . }RFkP!E;N xB:=ϠU$Eh H_f99Z|EGK'X>/ASf!" B^Meb1ZY1R A ܠ*.ٓ' L(Z s't0UcH74AxJgTi~]O_}Y_ׯ?7Q|_f⿥21ARNK U& NTc6\VtT0uEcLVY˖6Gh9Tf61mИŶS߻!`LҎ$(ddX jIZ^7hG KA(̳"~>R{cqEH DJ Ũ%ƫb9 NIћ`l[;Fo&A1u%+kWRjVdbC ҇۶X7#"ܬ,pX}Ҝ)/za-Q#\tNQ&`PKLD1." 3lL" >$iʆ mcjE=pe99XFViY%gjG`iq#l h,-%*`Tp~/n~{W؆_rQFjq^CoS(P x/lZ!nGH#c|@P)&x) '3/vk(::982fq&Չb~hvޢss~DX˸C=p,dlg,쌔,T|2Kl>-EXhԵ+Smפ7vcp..^a F{NmQU+Bk2ҥ Ч{ Sz(v|# _#(Il4a6dDʻz0V魔 uY[CWEciWoj$gŞ-SL0#GWC/kSXxah\A Jh9 m5:1w+jRRq 1ga0))cb1L46&mƿ0ߪûq]ddXMQ..@NOʡ9[Q2؊jp=}gqG"Hcox:WC+}9.N?آ7r̳ DhVij^uZb`Ա}5:Vh%2."՘2{%$'lg/CHDY[ZK;9F|,wfEꑤOnIqhsD/6u G`spi-t̵Z혭 4xu)R^Skh<~Y1҅o^7}ʹ%#=noQODցܽNe8kܮ[v]z_]{IMl_&{þIYKu9bա2δ50k6p9b OJv}ѯ6[0kdTĖ[7r0w71É,8\ TufjMV,ejSZhZ`fC5Nf=rq~l= IEs ׼٪OgD8 %v{[@46s `M3O|v޺c'ͩZI׼X(?"S5s,u{`d&=IUٽSdj#^ftTz] :{MfeIppEJuO8 IBAO`q&%7Lֿ$:L~$Xz0>) ZĿ͠K==a[ܧEd)G1x:pGƳ(b;絈0gnHK'&EEuYY͹;35od*,Hdɪ&0.mjk̔OI:aCΦ㰌a k5g}b^pD>|P}*!nN~˗`$t=b`,`}S0bhӧ4:!`񗲼\Ygž ܟK̃ohG%VWCKG“nW* w! ~ܫfHܽ91+Vhҋ8c8\q4s{ܮ0DG# u=]P蛾lc&{0/h}Rve$ PT- ź;S@ઁ!4zg2AVBQ$뤤#?O[Jsfy0$冰~7~p!ȹ])0r^F E2 5Æ[;en޼7H1-4&n1nZQq9 K jp{궣3^j@ TYqo.6-P|i6)RL! |A4-VgL-~ؾDBHcXGPPip?EB@ ~8b͌6*m<'.L{Gz2IH">kq )1|Ftsc? hBi y))q o>lHE^xPNN?.qפԛs|KPD6AB9byy̌ De{c+l2Pt_2ƁQOp$jcHbNsR"rvz36[{F5_ meQiUd3N)*7Q1qI=!ýs0>u@}~sN*Ic(3~,N?9#/VI;m-c/?-w_`eIlnq^V2ܓT1'i9ٖehrO27a|㰾kNm5k}RԧYݨڱW9KV[;4Cr2 TK90at|p&HR ^#<r֍r7ONdbwj4ǛmiL9żXF+xr)eM0zGI) K!|Sz:D_S8,7fN {աEAf"vRE&|p"TOg h|"&#c9ޖ1Ϫ^Z- yrH;l3:9&KeԚns'e/Fh[ /c ^?0sbSWq/cbSO^ٮq{]R@E!'ՙ&39Z=m0 z6\=Dm W Rx&/O^sYx05:Dž;-2B6!WZjn-QRFΠK $~5;.3l#z@úD ;=I.2.'kadۘBZO#`GʯC;_sMkMo._I/_#mQ0t-BoJ .:NȵGǎa#џGcXe_l"۸e'RO5sU5$ќK[tY1;<)bHnC-LDQ`*GceP>}S?'7y~QZSU S$IҍfyEG.[.{ZGj-@M>(4(83(l!ƉH 0$=#N1i~w/1N`3؟X8OhjѓH2O^ Xj27feLmL ͨ[b6"P-0,s|[8@)( (01ո0ŌnwMET,ȿT*# 3RrG@}=F4O"7Ncyu~GC1@Y'@I"dyyfZ`#ZOAE\M :Hot!5?,@oC ss?(^ىuRb,uĹ7ά9Co!K=vIؗ@v.B" 8ϳ{ԄQsmu Q|UPF~B~ŭSfZ€㡑`5hpH8q/;79{2 nzz^o0LGz\ TKE#1qԁ^ٴko>[BDHsi Z{"(˓$=al=yƖp֯5<އig4&Dd 亄<~4Өؐ{]M!p@w 4WB&OxrcƠ7\݌+l<"?xf*"[VTeC1޴?6t\u=rQmfӵjp|ճҷkՄ2:3O[q~ \9Gl#fPM0R,{/tY7G`\XޜV1͹5ZzFvŽgdLjjq*b5X} ~T֠z x(WkPn;p.4( Zެ5( qMx:ydŋXIѵ=[5qCT~[ 9Ui{v4Xj6. &+f H{7h8We[1Fs\AcLҹ7ge:<#nN).spn"Pddt$N& :,1Q챱0v5bs,<7V'Yl;gjGiu~6vjdUCqց8R]u7z嵪}f*6գ jUA;(b(gRw= %̋O^Wy#/f_H8A>* MzњHޥ%R0BEWV؟_nB&bw]pvWHoI{)do\Ppl,t#F=BN߸$ <;!Ȗ !M&IHc @.NNQyG Œ1vH\>O$~~`iLEHXv?l{Up_#/) ?l?v$>d pfZ~mX6\eKl .N%vR'$jK淁&p/>u;Ce)fm͚ڦQp,"<` &0Lg5mN VXnd}58-Ot-0D:-=?l;5Mu+Vj۵Rv/KO8^5KȈU?6g`PqaQt=čuFBULf,l5nB~RWb4I'H(f:n踹fz˼ns(r%K6!DuK~k7>6;𱹫tARր|+AZW45uί"?@DMQSv0421v=r pQ۫RR(]i{`X(YhEfsݐoIm!Nr堟}~Y Pw{ku֌P]TqTJQa{*62~G-w0$96Hd,P6]w"=tႉ@d`းPܮ.7skcw(xI-{0!K0H.[rlĝQ\ϋgs7z1u6t)s|sé{3 "af)G@ Fqq3 =vzAjoЛ(N!=áa%gВج6K?M.M8 O`J7׼cVw~e^n[ٿjDu897"oYySkR"?rػ^6mM^nB#ঙZ3?KMPo[D l $*tY-0PT3s|2tgAրrpڿ<8X[8vu8gǬdZ8Ջh'֝lN̑.CV?Z͛ kdq]F,vme% Eq@c{+Tc0uVoتV/M&:XygcBZVfߑiȰF0-Ms0q"Ͳ&#Z_j>2iB頶Jr-,_Tm$>j)18H!j~hbbbf9gMv?CFBh͐]*R䐀@cAϏ xFXЃD,HOn5vFCy&pg??CxQ:Vo2Mw$ܭߐ;<}X"Ŵ|?sN&2PIQ. v>>ZȇxD x=hR;ãPkFW4C0~ Dx5mٗ DIc1B̂Yd̹ m/yo2Rt@nh%`߳X~4[ENc[@ $܀PW\J޿]\4;$ }fNGXv:ȲCBl Lx]z )k؛>-q<t(ΠG'6LsD)BዥQdp`{ҫ)g|B>lUg_G`UǺf{KOӒc*҃Z] |?M&c'A4& &4țg$Wj-+JH.pq%d+oUˢbԮ-vmL8/B"-zXG4E 33Bja+C2 `[Vxr5 TçzC|$|z3S5D$xPS(XMH; X! ˋ;Rd5~L]]6/#Q6jЁ75$fKJJ_%EGB:{/E]>@Ewس8н5]-+ %PMkSȑ-O),dxPfiTx%+] #tfP2I aۢ)4wUbmd.SiO* n7۪ =">" M#\Zr-XMQfCtB~{S4U غ7sF|n7|<`*} !̛ܿ o5]%{/zK1v%~ Qh( +wtZRaG/' +gbj^"٧&n:<vq$Ԡ)s, Fue]i<1q$h) vRgx%7\Io}gmXbJbmyzhnhۘ.Qǭky2,:I_:KjS.r7ORpD濻uYn[fn8rO~ $ 0ZH݅^P@[| Ǵ;EwntN”1A0xp)j 8Pq]\e,~aR+]^m2DqpĿ| % P Պ:䅸Vp1j~WS'mbUʼn\XpbF0vW8r:ݽS vo Y MO {q$ݸ H3G^hvIV%r£aL_e͕H 8]*eЅ{sSEVm/tN ݢ`~  DcCk/{B !4 >Jп!i |T%K@Kڹh@x֮+tmtL7R&o`ˆ]بG\BeO] }x΋)deaTqW- x=,M\di`GC+0Z '=U.=j_jz뻵UA`8qO޾}olZWׄM t&|FlV= R*w%v&i6-A, 3yQnXJJS>S1tKzFc̣ĸԦo Oy<1zqхH5' Q5~kG j_r̳dzn;9.̼^ng2WBq#$bg]Q1 ki5\{c^_܎QIt_ V.ANh_((m{ ,-kяM^y7r41ƥ) qM! ]?)/$  ڏWc€h'?`Tz5 !ߏf&At>|6Q<-&|sQ`E.?@ih/Ec%e8˝|}!k`>@!6caV([J ]-.(QW/ܥ&w눽n#$Pព`Ԝ&ܬEr,2%̤媓냱jy=]_':wS#hJ?,bCHX u޾*?|xHaZA bY% <X& iMeƎSE:PxSAqdxTMo6W rlE("tsX JKD(R v[ۤg޼}H7v89v>-YU4AY#5]G {O/$?IӐ ;_ܕִI6)h)O}@ɺ I6eԞcҲL4rzOv9=cUM_U3~)tTݽURFTSr)FWst<+Qlqp'QT^?g*oI6rEԦ._PDp8oOm5Mp%]b%y>X1&fSw%2E(msÿYudz(/v8\P訠.,OKWƓاrNu+jm}>pwPnA-rY9Bi,XEpg@L"|TMf.r %D!EN䁩b6ӤSbm^#I[XIuA^Zj;e*kC? i|d*kpPPdQnD} k2tC&dw%ʦQnsm&L9`$طcuH\ωat =z-~vD'ir6:H9ċ'ΆAdw;!n-LWd8u1.HV{UU nᵢ>UNŴqzjP]\0t^U}n{VٚiE4xj-y'd0t>fhݷz?m5cCR*/ lG7N+/?xUR1Bc]CGB'4'tTYnL&lCz*PJހWEx^zo;O/wo^N{#ݹf7+oA>ѩi(۱mw*dyq xԚqջ6ikQC PD@Nn<}Pj"U؎PܐU 6ZXR@h՚ZĆzۃee2`-`ÎZYrX\0,E5ߣZ!(`kmFt[@@2<ٞ @9K EKEhxl(4H%_Y12toJeGixKB1vtGkM9E1i `>+-9 2͆LʔKx5L{ϏVep3 p ~8VHS Ù=KOxHxko+&sBɔE)"b#9[WegHK*q._8Z ˝{H:&@?j,S-9bG0INC) ,2"!ZA9! R¹ e>8q:<`a"*a"HV;y aijY0"bDA4cԅl'$W¬ t,(@ O ;x4ʦaYH X,8y)@S dӌNpl,wr=w%8N{2Nxi 7w`jFn[D~{+dop1Fh}=(u? Zxklt 3eӖHp5}RµZͦ;wqڑpTMyU h6vE7VeW:Oߜ`c `oZ&RuWQ<k&R%"*K\Ct!bjJ:I7d=hI&\ړӿLr-XZ:\rtqۻ-N\c3KΘ l#0ʺ:ظyZfW, KCTi<"uخp2Qø)w^w5vkb5j l"T\nX>:>9;_!E :.߈VRH@O %o j5|rI97Q:dZMH|]Ɍ5_fV2cr$ج,lTԀ.v[c k$l#2Ba[giq$ӢW ;#gc=߬qv~ybO'>&eEμq> cwЃ)eQytskv0;0:`NDy(3RdH:oq{:[{fȎVE~ e&6d7z6?β1Jtm)MgE5>7oxnWaw8{~Ջcրr^DfnJխ n;.ָEu!u)d5MUŕol_'٨Jy̚H; -(5QZŻǓr&S\ZNgxb)(n,zβU?2a(wUu$M̏a|= %%ِ9p(l r`+'̾#jd >BAf[8` O<{'>i/O= :qa.@-29|٤꾍J( _zhVe6_qtmڦۍ+w(G[T}3҇һ]Ǭֵ =~`60-DggxR1Lԫ׃Gu(=GĻJedj  6u*Ȓ{Ww^^pZK%S-_ߪwkANRΦ'Ee~јbp(䅒A@۳q9XyΒmPXp-n. ʧdgOy:wV5nuY]9R;0p0TLI9oS}"nw_peltOEW4Ty?b|N./ $pe/XyڮyE_yZ I%l6X;ie" bV0hjQ^ץa}$ǝ>QÔcL{@)zʨlQ}_O}k{ZOyԽKV:3,ݭ1)qP p]DҿsGtx1p~f3P\ŧ4AT=5:b'%j}p\m4B+k7 v8{\_ܘx7hr e3z=k%ׄ?YZy|/q)UxeQDt&?bI$! )x{reoF-C#(YB#x;tM̼dk..΢d[̂̒Ē"Dn%\E%Ey\ 9ZI5ل K dVQjb vt@BqfUj~ 4bM{8QxVao6lۢXvnXkl &K4EI)R#)o;JvSV`,Pݻw%'a0ӷb,c^2%?0/K% xǦ(=L&_(suHa 6Njc /uF)p®D6K頶 sSLH]ԊaYdYwƬTl4:~(*&wl \4 %9$ XMTL:o墡k5(Jp1? m!+gmѕt a|x )2$/py-)Y(?zhdoD6ܫmd]x<9jUrָGOdmaTR*C.5?"*̂RTP/!0jt8CҺx\`K2$+&uL f 4S8WWɣnxH]Fěf?'noNVgۀ'`7N,Hxxjo)V3Djj\Ɓ"^FՂ#&>pmLj5Z4sp>:We 萺 Nr?9=͌n;5k )?t_owLH]ɠ;_]愆 Z,x$f8,MOK-LVҜ./ 5"YRn̅ޜϮnYmx{[G8mgG\$ػ2 gm <  IHgF gc?KuwUuuu]6VVX/߈cw88kKOGPnMףb,6*͍j?~/'YOL; *}q+dSf'L'Y2J"p48ŗT*\u?1tƽLģP*N{i/~e&g@v䴗v봓D\5Eڹ=AxNpjN66NJ?$\e`G.әh76}ѨolE϶{)̊h}D-=Q ]~7q.&/R6 AP)Q1Gg~Z[',4t`Fu$]Gx|[;Ki,(mK? ]G"X+ʚȍ! ,]AWr79W*;Fott͇.]Yh-oU?>qN:;+ zeyr# ~$.1I8sJt.1{󨛎,HD8&Ӑm0k8::&C1LM$4_K4dck =!O2K3tpz02]U rSC <9=tyzMlcIh" V^|.d0t%HrQGL_޽E<zx ~//Wu@/vc QsQ,.>\Mq aZ.8OU?~8˛\_l4 @c~ #dKT9߯5q~L_<|HV *gXݭGŽyW'@E1~*Xky,(Dq.ڝ_{&ڻo?2OOr)^4c/( l!'λOx,DB4B^z,ϳ[z-ķx,l,e!lmx-D?%IsIgwjS;ywF# :l >:| \:8[ϴ?|-Y\j*\2ڣ;͞;>>j4Gּo?~XX}>C^w(ӷ8 .syͿB _4(¨? yvI | *9ז?nn^dn܅%;Kw2G(_ĸB߿Go1śowyMlomnׯޮ~C1/g*6F9e[z?٣Fgho=Q8| y_;A_xR:'[<'kO>ۍiр_Q_33O7? .v>zGoYSǣk ɖt Yd).$al/L N{ NH8؀kƢ(F}{muCOӪX*C8G'ođxmOѰj܋׭xI_pP8֖uY-M/qw9e P3_|Sׄ c|: ȅtfpN<-VĚDx");ZpI2V F$ asoh|rQ ̋-AXh̟6KB2JAл.F۲˄k_3y`YYrfC!oV0iOI0Qw.ת{x T*'Ds|^ 6FQ0.*h Mxj?#!iwËc(A<'y8|oNrB(D,%Ǡ}-ǡ_H j6Z1 'iN!F"j\n Kڙ"Wy17"rYܰ[_ z 2κvC.*??W#^+ B K~E*}Uj$Ⱋo&aզ|"p=7c%-:/7rX9qS+rIyyqW^ݬ\fy.w#'z34_¬,-U/=ݞ=֧wKӎ^IClxGPBʨ~qNH:UJrg4%Peյ^،W齑C^ a <@\3>[7gss.z.Z3XTH#>%uF5V:ɫMQl3hN"@DڗQNR^c1vRQ!L7c8 NIE nƎ?+nإ ~+9`ʲXⱫRdi'.ڶa^MC&w8Yw >W28Z I}x=Qܫ($ ZqVdLqp*O&V ڱm]Pl.2˜!\4K:@=qiJ(E,ܴDFFPklTku*U~K~ec2ۚs4dCRjl9bE^}ɆXuɃn3T J]0#IkYw/5ugs*nBU)ޝ*4OExkZ}dͮ-jZhI։z Ysc' _kR>V$Jx|+99VU4k3m&`כSrnekm, o0"LUcN5#;8'0R9Xh*s /'ЍExgޟ mFBih`-dQ79dNaz~4Sc~Ƶ }~fGEa鎀&#o6q1G^IQs B ^vMR$%,^ŽIb{~g8řŊTxs4|x ˸|,6V?,d3ƶ@}nw%7p~:,=ivӸ_oe-V d^}x#ėòv^},v3 h?]6LSY"l7W| >'ͮ}ۆE˗ W{s^=6׊KGU½v 3ꑔRmlSQ5BY?Z5#,j(pE Z#dF\CR-ҬV឵ c .nãDҹh=(HWð76jޙaR}&ZOʓ3Ҟ`o6 $X)biWgh *\^4鞾}D_x]#*X/韏/p[>~(^!BxÕ?yҗ'iVIGQVsup:8mŨq0:769-&..gaUz]2˫B&'8iuLM|+RyyHYA-dS7qU>#%ʫ7^Gkq:2'k,i=aHQ u}'OPoHmLabX,`mq8>uz*u>&u{mOFs*GoW&gJi&QN_3=MẂO[m.,( +:,p̪?FJ2QZUE'=3wFa WʻMLVzy{anՈng.qd2>Xnj?7_TkB'Ho {;ijy"Si~>dIS"_a ]0}GU,_WdʀȷI%Oxțr6LLqKZl0ذ82EM[sXpr'Hg}iAx_)Z.}nVUW$5 JHmis%_˓qt<t5Pw >5~Hgټf[[I G03ygpb~-j(2⭛CZb6l SgECj ϿDba/NGe+\ &h |1EyJWZNedAtPd{Aհva&bX rl`{2thl ^se~yϳDD7]"I+Z"nq^xa伽UU8|XǞڂX*.*kXju *cݼLh!_P ̜ʂ%Asa kZA'Zl:[V:[qr9\%'؍9?_Y5W6u0r+rt~k/vhtxg`3!iDL]2>b"[+纱͌>\x*f&򓨚l^c|XJ]?jPU0֌jD]a_2GΒaHKȳk7[+VRD,G3tot#ZpM|5x+kT^Zh1e%Z)VU N\2 f({[ZHᆭ;PӐ1z !lUxf;~ƾ2. m=!GHXM\eݿC#pN |Ųi?Gh.?++vFN,6Bot=kOaC/c+U}095'{:vEʙdqW{t !+]ظ/3`+<[͓|/M&cE&M&At&Vt5h$ >׿)mA  !?Si!H9;!1<+0[1Q2ʙWm8\WU;-UtW8䲨^i@LFHfS ܲ1!p& AbB Gr\oLpi/b[(KyM n )A)gbd"oQZqܘ}1\߰nuw*OʲW&&k t Ū@M`zË8JN2տ{nK5a7XЪSD7gP"R]5"9^բ7>Be(QgmϞdnмu8_XʎƲXt.HhA:^9z o9T)@%sUԿzJYw#ɰX6/3kjG7 |O٪;:rũtYgW9+į YeL5j ~@<.%:g4ibR;j_oV-\OJa|(ZK+) ߰K)մxTG°,$9r.&F[4+{+4]AXY*k}' xy۱砑Iх 顈क़"sO8dsB8jڍhِc23=\k(%IXeWxeBR %:#]U|va4臘AN{F#ui.G4 +w~~VOMKĎsP Yi|L6tA%Śp V ۃYhk3]Vu>M1B[eիk"DdQRRk zc4s}DquFXqE5^-BN`8ŤWPaG7S**X(LArVf77q9ʸG~ǧUPLGWd쥛sTc.) 7|<4k`<ΏmYH1NnN!Q6}*ԕ"k3V%Y4dܞ@jN!9_[m[B߻^ oNzmU^Jݐ|Z_-,7V.e E=3qw'6!@;٣5y68d4raPu˦rRJة*\{\rwG8O7r'0GǙπ\g^yq5ߢlÓXg@ xHc{vdM \}\j`hʨLtXbP10M-+#cW&d$kaGsՅc!Tb3% 9BK x- J>TLLA{.t̮]%^S񝒽#^܂v¤rM;L{xېy||xʳ-:J-[AoC]G)8~L~$ChYֱ܂V,T=$Mܙߐ}ι!/^moBy.=מ90 6\v3eeXӁ_wUslJx߬EHG/)2G2JȊ%@F7=%\sv'v #е ]\@X SjvB.oO^ds[DYR*4ڂGH/GqB1D3X;mV 47@kh܅rZSZ`ZJ #NF?9]̑ÙFfaF;q{!$(tD<Za:<j:EQ2RvBoM{hNIq-lix`b U*?יNzxQ& j1 uQ5ӑXojW2=In?_|jw!wsW0OflٙUQQee*PÃW+;8VmtGjƕfO=gB{y4KjNg;:h/F40m C%1>Uz&^8eX) XlԀTC jts[MQ.Hw,k/&hHVV)͞RIH^@{{hZjQHFRqXoM~ĊВ/- ;8<{?~ #bv}fA ǥ>Rұr 0bxpѫZ x{]}'+VA%;3'E%+E.# SCnfv[`+(X"gP@|a-33]TO|mMr!_ Z]5TH5J2#^;H.XdkEB(/Ey oyۊ(po޾>ُ{ͣ<4O޶eR2#+ZⰑקoU{Fly ҆J,D6I2V@c(/c09 a CBw f\4xJ6`BqQKK 懘S%B Išiw0[JXJ٭]͋GzX^Jռn`uNKska2쒬3!_D E0 O$;:>|+f3U;ϵbhݬ>mISgOYD*)'ouIxkp {:c3 MÌaEфEeRBM,8eШx[=p˂u^fާ>\S|Y; Jj3)@@% `:n7%cJ@Rh15G_&>Bף9ݜnY8Kǭo_ɐ^g|]s;}~7JeJǗ{vN `uE˻iWbsݐ(BH!,Ix eH2xۑ=b?,xmf!p) EBWwuۓ;ꟙrەQgkˤELM7rs.,,#I"-a\ȇgjZaŷW#߄}w[-ygMp-fhm6V,)&{8~~H3een1sD*\kB!5ΖO^ըOH\CTÆX*7a ',n^+oSe ߅|QjV^i'9,bzеM êdkyTCv& bzhI ^6KʑY7y(Gtz9Sۭ6n.~u wϼۡA5s6. v*rѝO|;=şE ʲ+4M{cÊ랏^C(NqgBGcs+h!P|]".GGQ(t47m5Ҧ1xߜ93HzR?ӲI\AŦBA$NE 5}p*:yɀ]c,¹;AJ'FU,#},6z=^16?c叝~)Vf@חf)ct8MX{ݤGg]oY]&KYk{\~W_@™&}Ӟ$/DK  (7If›\|ck1/) z,jc]1Iq'IǕ1d&r͙7`󦵷?r{G?_'}B47[[ؿL mE ~_|`2 "Ѩu1Y ?Dw?lzg>~{dTXG֞;VEAV󤠄2O Jhmqǀ9YwHi8A"Lb2"T97s_nya`^'ǷE+e$Y6 ȯĭ r,x4jrfvg9rEldC$㌗2Ŀ>ճr8}Q>I @/?$- s ni6R/r xxg0VRWL"x74Bug])p]FqdGp@b ,ʒS"ķôø@uickQWk/zKyvi_3ky_s˥N,>봻ŜX嚾z:%;7 tϨ-ْagdPM,rK,jE!)$elRg-9Wtd r-=`V'uXQJPeb? L2k/N'3x3zA5tp2²~8V癑,*Ikb1`Mu!mהs# /6mCd Igf״u&_/nR/8o2X[ݓ;PsE mİOjцfш6ĺadRͳ`nlr}NYwow(i%ӟy%{.)x*3K<[;_}n>o3QmGז3`a&#y B5pN8r_}m/3~'v~:j쵐_ -4 tfr>,V9LhI2,{%MB eә ަ7TO\ xݔ{T8ΰCq>8f0:`%X۱b/p3O<ݎ.$ 3\֗hLs3,?:gl]KVOBʒ,YY mm#Wu{{oف@\=;yȫ-7X~La!VSdB#QmvdOm/ }T[h/ ۴P 7ʸkT-%$j5Bakn!Q}aO~y~od[7GױS1?^_i~F:oH ;)/L'V ^؊޾9URWD-9M-)$Jp cUaSy\ka&w'88 x389,LX.4ǕGGT*`Q(@|^o[(MymH7l45M|+f RZkLB5/ 0'A9i|B4Ō20SwM+_T[*!mP0򝶻H->a+Kk,PGecSަTsVIJ+N*?S2kB8{%ِ=@F/KJ.3Kt:j&㲇y7 ZoEr]&,BFzW,Ԁ]IUprD(NZ^fz; 3RԬ;#᭦{͘!#Hĸ`Ӳ^@0$n?@'"&p)b׎߶-bސK0(zM|WyA<@ףa Ʃ.s| |&c@>+q7蔗tƃQ?E E,RJ8UcCۿ ڇeFp]gl&)nguEUffp++n豏 U,͝)%Y ߒ~7=SmN.U"`< pkp6KvXXN{a?~U߻ ,U( ejĂ$V9|/Wڨ8؃/:/JF6+C^Ő;mm-& eVUdKV^8!B&k=|G>'xWl_> ć Rvg-}:{QoG-XWEEX*p^OZ+CX!5bCɨӶo5 ,"|Yhe!PkEVQn(D!@Ķġ alf]su)RKo/ /*3Pr[gNjɜq&)ͻHRѓCͭ$ȰC'rs;u~=s?$~ϟh`&0ߖ*leJb1Mj΢+X7rWaI]NM!Vm} c>+Q9kۡsH{?vIvuoSt# Fc}!O@ύdC<DE3mzFyundup5hg:bR[w ǚeL"|fjK"/ 7ZBp|8%N&w@ dNm4$f$Y>X4gW9%ZcGWcA,VEVi{%tam= Q2}[[Srg ?jb6yY3VݶxU]LUzڊ^r{&/MLvM;A~H왌:yJ4&S^^u#>em2Q#e:R Ã?Vo·j?r8d3]APT*/n^kn8p{_ǭ=7?8l[ Gj7ތVoA?4{njB9=3sJ>7O~@m ȴQ)$6 )Odj6)4XO 3 .훒X+i3l ;~[7 (󓠹(/b1<8õL#rVQq2Wô!󪭽/tkjǭ 71ﴚNPLy;M(dŻ͟Ntrttx|ړI\ 3yX<]02HC"=pHKL#2CIJO3 'M瀡eX(dk6KKWůcc𒱃mĹOc4qÄ/&_{AC['n i@o!ju;+WU!%:~c_;a#HYמԄNy^#4soC'?/z媆 !>(šmT2 [^r PeHXѽdؼ[`L8h؈TCE8HЗ(pQ ڱ[^[gѺ׵sQ-&[2rUSLm,1P]$W,5cx۝ҷfLhen^-B?k{1~1{e .ځ_?ڂ;G/SwY#gZRv~3?l׿}\2\6GiِQZ0ۼԑeSs~E[kѽ^ y|I[ Vt"\ޜœ8Gοm\*NzE/hz- (Xl$ldݟ$B 'S;X1oT&* O0ROȈ]xS~*uMO;dNTV&X.`xlcjCz̤TRAYA~dr0Hb+~{mz_BG <| qo[p_ϰJsSmFMYGI4WsO J3|t9l%VQ[6`ۀ*`7d|3ǖi*5!L>ʡ㈇d85>Ů2=QۡqTؖ=`y3'#yh$s7槺aHiA~@ fU}~DqV ؎ƖޣW0< ",*CQuXيHk28 $_%&ҐAͿ Ϥxbd@hY2AM5)| vJTgKTI[_LAbjdxY ߴٱ?Bo6U|BW00OQ4$v}vJ |xN7L5G3/?F٩B)r6)d<|H(R~CG.s+g\۝,o"  DA-wBJU@G+/k6jAt Jz V/0鏪0n+b4ߵLEMR*SmFdEN%# D1EL:5TyVZk'is98.n@24%ʙbMmNab)J n>+liٲҎD2{Gprnúi}@V=ZJK?jJ\z-’/Lg=sNZo=; M rhEjnܡ  "8߷wd+ДQ/Pjӥ\I;>HC!jQqȧ4 /'1f=2 _#`ԺG p2A 5[?Vl=Pt$*[qSqrJGXfE+k?u-3ɮ4W,M0XtdNj:&3u`rh)ko͹ó^r҆IV 9C@V*vC dXod%&ȵI3 @x4f0%di⽽R/ry<%rX";iglb'.\ڬH dO]qp':Ɣ]1ɕZÙ>iTID?B "IȩMA6ĩ] EpIhJi9 ϵ(XZrXa 4b/Rd'{qD]C-%\&=zIVo <զ;&cb6ԘOߒIWlKxv)Z&J'-)% B'%BlTU1\tF"AZOhP]VȉzS-6P9Txg4TΗj S0rL;BF62S1V:3R~mxqmR_Y8H@UMhce/0Y8;SS U~p#8$<ˎR\kh,! R〤 -^ƔkJ;co"ʨh^WpTjֲ8Y6dXL,Ϻ| Li3IeOl(7l+C%Nqx:h߻wh(nd &,ʪ+r~EP{i?<2ܜ3 ܊^wcIcl,+0px.t34` 涡E7maanIaDʼnoOtޓ0ب*}v]/WE꼿@EwCHe!tfqK潺=9={!MN**2w./pd)%<|HV>PX[)\HBo&>)Kt]JԆbg68:w3|YG#2mԍ˟l7yk3^aN//CcTDVvhs y&GG6G͛Gg9hܑCF#so}Q߭-.r"mp>Ll^nzrv{(_7, a|s$U2rxd^g\p◮bKX#^(h8-ظ щ+iu*fQ[$\Y%%7w olK#W^qyTw7OWܔ9^7jwtY~8`غVP`reLQ* 3 5bfa)4_ӻw4F}ܫ. T]m݁wYRKcPwed$ԗ0{FAJlwWaG#goٵ0ǡ( !Z GqG8S*@yIBȌN dާ6~HOR]!Q1A4tVr$/CM.nYE? J{`̾"c.ʍ>0. +'*[/;W֒pJU(͓i@CItήUͻ+ v+; ̓ym'mF~{_$4y@1H{E1 ($5yTqS3z/ QY{AXBՔ)gUWU??!1kbsRPQ]b^.Ɠf^r`{L|]R~Ϣ֥\3@ u u4\VWQEESE*ѨS]u].ͥ5䒐Zˤ2ƴdv~ _W&JBzI筜$*V^xg/ڽjtf|9-לt1OWrd5+2zQq̤l;`f)1TV^¹9WlAPI*kcGs;a8C.,6IΣѦs.ӧ[A*.Z70f큎ڋ ꜝ^ .[jr){uptxNB6Ʋ2oͮo2Vuv}j -~Q< tFֺhWJ#5Bl<_ zo~n?3 )gon7-LY-ޮxp) !߱{f._|R=d(9|?Buoh(W *{)2+QVtvxNP91: pJ6pMp9l-ROs3޷^7b-&.b`>-+`v=8EZ=Ys`Z\JЗDe}yF CжWXJx.-# @߇%=+JPP^ A!lep-Rc|J쌂U!pIj$JtRy}D jϰκXV==:[񇁻&J&()%$)>ݢⶬb8$>aYӹ &.$Sb_5Hȗ{=:"Pauw(`ԝ\QK?1ЍT>-}:X,CYDfG򗃾VWf:] 9J+qp#}cnv*ﮊ*@.9ASxBk?ˠRL+;e{ #̌UӇ:#B[R:ڐ]XTmiǀJ!ϾK6.h{RpZ,b"ۯ$>rK\wX:2}&ƶ,cYm䠬hlǻ\3?49qR0':aHd.mn0ZXH#x|]Pґ1ઞ=lAn dUazU*oq'acNSX-J뇺*Y+ QAs%XOQYS}2:*aWi b׹X%/e6{Is3mnБ/ 3-žθx,R喲|:jt</ǥxJKy~iKEFؔy'eAٟ%oDD&620VL,U?58"whs e6󌹉1̍ 7L*"P4,TV%M2(-qBi 6Z?b%@oF-KZmC2}"׫? ,K.$qѺ j@lLMKi W/K~}Ы.uch9F*G9roSRssWs`/5t`aqd7%K46zt|x>RG%EuQx=yS?Ŕy:ʣRAIMz5uC˒$h6 A\WhB9N8k.OpR|,/dn?=-f/tp uƪ~{ttx|ҒTvVǷ3y{Co!Ƚd՜=p5yc$́Ӝcc nMnI< ß[< ptxYD0Miy+ Ob3\pUD0w\mp1Xoʚ˝˞ ; cjqQ{ݦ]wH ҁX9fǎ;)hvSWQl\6P TbG]ܣ[7 z-5 k+5^c *V-ǎSƪV؟l]P+eu^)tXbnF[7]Mhk5 ) P#\򋔫ZvrC k"6 4^)35΋q)(K¹j7Ədxغ)~l ?nNh$lz6^+y^ רn6H9H%`*=HWNpv:@ƥy烟ל=euTԵx)|ptIAgЃ/$Z܌Eyk,&IF$/7SP[ J۰HYQYe?}tg2SsУV%QS(·9"D+9Xh\DTU[0pOZΧtg*ۢpkWA (!cAh14Hxٷ j)D0cw7TԻ 5ٽpl51ϊvw cU^1|`QZl0jEswS'AȓfbR38-gQL/ڢBܐ΋׎",,FȑG6@̸13}R|ZHPB4"TQ]kE*\b4[ KQ, PGo(#KރJVf8}[}^1:??DgQ^zʲႂw_y pi#+rapxϕGnU)wo\ ܲeA ȴ"p*,BtwXݲЬ ;s0r2!%4eYR-էXuRsfycu#84[p(}q<;'6ܬXvY;j4Zy4H΁Kٲ|UWsB}홺%/XVk8R7.j=8B52S#72y+Qkiǡ}I]QOsϮK|Z7T"pA- |m,OTRfv٦(D6օkƧvef]_fś"[ju…y9c7 &Z0v|2ƌߩ&!}39,zab˓%d6ẹR'A>=縪 s~pDG:!(7bmk5P-јwzvޓcޔ)0X)C[b ɛ"G+gSr%Y*lG-֥M9m~ 1}Z>tᾼ`j}le;4==:w1_RxGe#~ZUx6cŢUXȠI:i_0³g*Y'G&n\vqo:@SL}]c@uSU_4pAѳ "t%Tm 4xRꮚ4(ĉ43+9+ś W/;u;8HDI()gP9ة`dR]4r"D/;U; yaO'x39?e0m9?)"JyljF_LK Sf (}1H} jiVTjke KcIN}}<Z3Qb߉j]$8=fG߈Ҝoð͆ i+B$쉷PP)Q^:1{SyɄM3&1:fKsM]=#䚗}`Jc]ߵ ^Jb͆V!w3!W\'Wjl%& *{BW;\٬iq "6뿟[\v[ro桎}KױGѠ_ ;gfӮ7; I0DC(3 u*2_$Z+&qGei:A j6\yjtk.nB 1rk[E@v\shjVFZ>-|kȧN1v7pvG{bNhňBz+^uġkJ/F*M#?tf&oUM2ڃÔqM7n (堋%3]3Ejq\k)+LJ|}sa ';- ]Ś8[(]Skpp 6}kQ쓛3/E N&e1R=E2cV뜞zs$[*k;v: 0̝ģ!7  33̢Ry< D\0uޛKURmOTg&;ʼqBvUo'AZ"4;I7ӄhlJ0L ;\OC]Lhߥ96fP;Rk1dVL{.)DW1FAEjv!ۻƘT;((5\go^ok#xx}\GxW`? WepQH"Kf3`/( mTgs5XmӽZ6x5J|:~w"NnD#T8rUoFzs|\o7${hdl s$b1+kڅ) `14l0`ޓDӯS"6/[F{\~i8åDHʴAWJ4ɮn!$.Uw_KãDa{r ? V50G򀃷2AN̰rٸ,Xv5_ZY!GzkV%*0ne/w 2ynSO'+&lyg7;"y֓wFkRe'`2yexrl?n<Ѡքi[=T !nb3%ٴt] 9U `ZKej7Yj76^V{Yl2[-d7EqdE%ovqs~:VBvY <鈣ѫZ&ydJ@>$dHJnk˜ѸS&j3b9ʬT*N_(]s(f *#xښRLt"ucXD*#Q(n@$%f胻6?[px E_en3X=0б(>f<69zDe&0O"X@MU?%Op1⌘9+,1/Yُ]¼-"ZnUZ$],GouP4҅Y榫/!R`\喉0"^Oxqr~FPw;vB; W.8wNƦ [4>=}7o8w+e[ƪĉBē8޵E Į~ XTAT)~rtڄW',Kq`p4|A RZ4rg'Q?>Ue`|:)`h7£Ӊ OO6w7@1a78o:ۣgq_4,:A]~ ?zm HIg 2W+I,e >9%a$5^.p/xXc jyApeCxtg? ;q/52g`2 O"3u48٭~)nU͂Ԇ붙ؤLWƣ+6hvv;Q\XD|RedLv{R(V#8]b@$GiVp@I/ ? J(-^^n+M{W@g 8_ !s%4K0vQ@v|!xg/"%󂞠KB t,,ل݀BgM\`=u#^ȿL֎` ޷(BwkQA&0{xyg'&J0Q7˓fejփGU?'cF&,|'=/.ktl0e=Z%o ul+F!MU9R786އ%hJfk``/)1$z.v=»OYj"l?lX~i-enٌjSɏle>e6%X;"awGT$c¸)|~rZ?iI2'<9$enf뀇9 /5ٖ[M3{GBP9~1=v;vqȾpٚIo$臛֜ 9Oz%d?nS}+{>ED\ϸχI@xflz &>+V!vqq"i5b&cd*C2ٟ9ݛ\:*If0*3dp4f4yܟ 1-mRRz(R53BHvLVL+nXc_2j%EH aɴ h0rJ gt"^C%95ƽ L;CvW.h]`s rĢ;A>qB͙=&)`GO-iw7!]cۃc4ӓ xmpnmq ``}2``&2NJhZg6=uZ՚ tXṡFVͪ 4*WL +L~2L.;|!N>Mǂ!f^)ۂ_۴R;sJe.e}M-BU'j6>3 ؁x@KIgv\` (OL[TsϪ-c -W}ǘ7Z;!WA~x8ex:.;%ϛOM,z)Ž]Hg@W 1? f/5}̄f%gAxT*U={rz\?|;Е_8?V b|/o]-q=%8@wCgN`TP}SFxg4ɤ򄏠pb*!e<,,p4N̵V iӭ'W׻lzf23 R3Ä  !%Z{ .AHY]tq9BOhfU90g x0_QBX'J%[H( Gt`">^D#ATѫPXb~8NW)ed1_JTP5WJU*7r2ǖG9 qy`wtZH30[~i0C@t丄Y`1,>lwЁz睂[;x(5Q |N=[̂;kb4) u-`[++:VpB Fl|Z6y8yb5?/L p;/kjd-bf^SF5n72X|0ypF1K~kӏfFӨd :Aef ! ,'{9aPj[xMkӔVXŊ|{%4+_Z߫q2]RNE}ٔw 8[7ezru kzfIo0X;IQt3kO1֠ Gq!CzqQ½4g0!O2XՑx@%ͨ-/hطJԥ@D?W))ԇG)N Òk? 6ϴa#h4њrNzkodl*0V-(2h*غYJu*eGItIdXH "th!kQGj|#er%ƃ!*m}qxXھCuFc8!{Z6ʪwOSZN0vkK g *0(W|DT=#X٨/Qw<1_v EJJlGOtsG[ k9]i+giMSt-V4HJmgX"AЧK1 `Z`""zDu|N+6z'&Z7uj(/He#;)_mOФF+K*OFт+C=J$8{T|kΑ^x1f"p>+̠6&߸mɣQ򽑉Ç}]]qX@ ̕z~xzT<ǒ0%Oj)~Rݜ^5N_wnܧ-FI){hV8m)%ڲ͖/ 1@OJ *B Euϳ} ,,+5\!#W򶜛|{J$h׷[ɮ +I,&-$x3K MX}W oKxKᄏZa}Tǵ^*V)v8#-aP[RrdyxģERh*N.f1= z)U:yls}80>2z*v[f7o= K%')ƀ/M3ib8&L"&2.9g恛k+A-ٕԫ=S2>zё\F:w#xl`rMWrWْ1llܱ@Ex/'HE}= E+_;ܳƣѬ; pqww@+-TxB--RYkYxPK5˓jY_ eh]cHv>υ11:m?>qsL5?fs(I@P:f6| > "]cȗ; qZ hMPjY!Иk-qI.JYU\ )Z:Ҧ;7b`|)9aA,ހ2];:]e57XͶ~{SC(4 G%&yK/3ǂz XxhպN'Oa70} GBi/xk(ޒW Kh0A4/~?8~4xD Kcnȱ7j/:6+ke`v®"L;+g<7ʴǵ!bԠ[0fiSaԬ7񗮄(R͈zF _0>g$Tmȧ\c׼B䎻bmъirUl*6+|C! pWJHeVU̇xQCIsOq jj&ysUUϱnt`oШ\- A@y c-tKpKw.A+ l#zvѣ!wXj &eu,_\$T8O}gQ"ӆ{Hr* # ftX%:T.tնv0O^$*oJ[#΁̌6t?mb.s_xe?k<)XanD傁UsxӸ'+~\Znfy\#IS(͸eA:pCYP7fsbؙÄ( $'"i4< ^6z\7~'h v9wn3n xeRYe&ZQXXRr1 }4Wɾ95/hb5u7t0G^XE4ճb%8l:VlM'#&*}f1\ ?5]~p,g aW;NR캉zvׂSOο·|%hR®OGax6*B&bɢxtWn"d6 'FLUDنZ0]%s:/ (ed[_9*.M-(^Z, in\0]o|,CMOd9]f\r#Dn}91ҍqeթ3No0)uNK +qe)}jqsNedPߣ9x墎/]J}z.gJPJQJ-yʨvuBB%4[L"Dq408r(Ury+')a. mo,/-SUh)bkcfِV@P;)5=pL(o1D@͞9'4rZ e)zmN-V7kBn?o߂oߐyqD?_ǡbZQ8it8ccvQ{o34St\ֶ}s{7G֗xv FW!*ӹ ,rdo1OpvqM: cu~S,N!W$^|$ƙ\Gޕzpl("ky/NכM|hZ5՟9kX^sZ(=Ah5FU)$5ӧR)eo٤4S+=Wf_p dfYf8kR߶u/g7?'qp=2ojۛAa&(άhk"^k6l^L57@FWd]_;q< ԓT~ V,6V8UU6v|Qq+UV^mz2 f],*4=RFndB>cm DI":BlC 0VwYf._PE)h"R49 +ԛGި 1y2fM7)M=Q"1QaXZ'*.~=v~w*ūPOC'N7#\NeqgGGQu$B *$BUؿDM(ɾ\J&אC(5-ǜOsvnWG|R ڧn+l琚nOڍayh%pTu=YRui:&*Jvn' >aIdWҌ2KQģ.5r7& oyJ5ugτoa,Z 7h{G$\fyg rggu*"!,a,_ve^-{.7tXA4WЍVNKg KZ:gi~}X{]`D`05Qd8]o%P/"Ǒ0u3^䵹e8% Q LQ 3"\q8{gOyqsV*cJ:)]E&sva8^K FTQc#}u{X/GziZ!r^.$'whpk8d!Yhq} lMMV̪VT^tW3SCD`6sT1)7{cɓkZ^>mmNYޔk4pʅi(|,OYd^ߞ#Y-ǮX/fWZi`deo:UT%tVy3 ̚i;Lޯ]p~?Yaݥ?y);ڈ)AE9!hd&t(%Kd7)`6B'8 yM ! /t9UH`ЙKAETc&@fjN(3oޤ@G#0|gԇGL%&F ^}xF}R#bױ;>mZ" Z u""h8Ǔ$v23G@`+[d4sGxĤ$v+@aE@f퀯r#V͚s$8 !h12KdjP6mAP2Ig( W EmD&8 0 )me,wٿi.B9-Du1$nߞo 0j/kl]ɯҺ$kT23br [`J*ObodY*$g9_uG}ty hxJ-$,ݹv8LoYjIކ cw[ |4KFy:s-8͎W,&/bJL,)iEytDkdRm"Id. e4h<{y[!{m ]ёqEԳ/~$)8Iu4IV{|!Ym"tW$,2!Ǐ)E5I^ves,>bM@:Ok]]͸5BLu'ܔud)8NOπ~*z|#ZU-FԷ .8tuȘТfEt02 q*8u ,FO (qUL }7%o}+D IՑ⸬`!eͬ J7_oAUc^~7GN^d۷>Lg ez#Qɳ_Eu|g>|~3G̖~rCmk1;go_fb擨_JQthyWDgv\1am^)/ozx)ha<`{2Zf Enm pfCQQU`ہ^(CSl哉.2.sؙGqj*`-%r0 I2.z/b1m$qGCN܃cEVWp"l׏4$pDCM a̰܍SEMk4b<Q%+/_:H0MtSI$m'ӞWykʰ5"# ]UJߝ`lT;̻O~/1 miht\{l`1](~#]rQJ=ʪ%zf[$G 7cCIgi  Nd`^;cT-eƎ8K6ލE1q 82>qrl4UfIL@ƶڕNWFkFwlЗ6Z#^F3CfάSJ^@1m)I٨8 ~0ŽT0gύAbd95d%Z<?4NC$s3 uxD -fD@H/J* KKK$ !Y3 kNܫ-TxqI c'0 krqr%**Όq0nsWݿ&3rn> 궗31x;bv" ^%,]Z"1Vߗc%ʍJؿ_єM2O{+FCey~ޞQirc3qTI#|P}5v:?3n77=ڝƀEL[71)W0aJ2*Nm̵=̞Ivxuq "&߷\9q2od(sظ8($)Zkk_Y:yr^vF텺L u3ycx:}pםbm1-Yرk !E*ڕ $`M{MIw޵37I{wmzM.Q!Gzwm.WBҏ\6̅&sݕV_Nȶvp_ JORTH'eŧ~$aW|H|\x~)*+Qg@ '.b gZsN. QQ(޾uI@AX  WɞU݅25'9 !$ńiNO1 ]A܍NB+GgJ9LY&[\H'LÒ,y\j G֢R'rShr~}Y%jq;D4?"|lUem 4/%{ q'`7Gdw?mڒ yz]#">d'='? oN6c.u/KXXPMueER~ƴ" ĕi?iЅ8FNQ}~t~FH)/p ]¶ mGگ тoXզ)Ц6}ff6\Cr'v&1زɲG"dC03b#>0V3D)jϘJ:B d}ѠCplyj㽡œfr$qiP#''x0u 6FCC ?d{JVD,&~]kvnݷiO'B!2z68\"p!"u۾~8ՙ}3.8E,üTiO*@:1H_RLK 5_Uu]ep*:/1c}I{]w*b~X`T?zQiXW?ubhaf';3H՜|ip׆nK%Ȉɢf\.Ӛ+?e+?q[ѿ;o߸dNxqo6')*9$n60=:wurV5RhY6I]\G=4Ւa9}DbNd6fCݯ 2$J*-{x) q,,3hz!<" bv#236b, @؝̄İJ^PpV'jB;>36J a"X%t`"WHLr|l1d"SՒdፒ@35OvȠ`; : kY&!W( OV-%,:tKՊNk("YFtkL&G BtQD#@ϚvZbVmT/P9IC4)t.Ojh+ԚF[Fq%d&IJܵYI: `=&Eg^]j@ҌLMiI@ s:LԈ7eplۦHS'f ,EgʓyDf( L؈8C1BDټ:8^Zް z%\j4@ة+h 4ճ nHN" %+IBdh6"^B<.CB{6E[NrJDHzA h?)B.Oj\``s◫`-"*l w[\8pdgz_3 2:v iF>7?WKUfDœMRjbօpv25EJ,̢)+bt$x$?o3W|b mIS2Ѥn#A.rĚ5+âR`vjK=utlzlT{gB&N&sVԉFX5@?R$4)ЋMv'`.<doy <}rh*)aN [ut^6MmRMT'B YRkWxBŻRHcYj!#0ᜣhYl` ˓k'.ЎR6u7)2vTmܚ+q0s\{_S?ɢnĠ'8:Ӫ0Lȍ,f,\G6Ӯy"K[4#iB:0 w.a&<Gg!=Qx$mws/,ҋZAD;{;xBǡ5kW>98F(l2ƺͷb/fȴ dq> K[ODyyķ G?ZRPJ3Nڑ`^h/p]+~ cZH1N/Y4Fr0f\95UњDXaeR8/!^4#҆c@uO&"_bA1B dTE!IF?υnbYJb V'G"j8>7*9hI5 BQv:/1KN όU v\H|*noˮRd@MOk .ny4lv~ ͉lWZfTThVܻ2N͗x& Kl k$Z3"bp/K=~?J<>ep(MãX -_$*^vh;TSŻ5]%/s=4F6J!u6C!ބ!,8`0,F97Sѱmx{$!bJh>b]оo6ྀ/F]?-s7\|`muя?l;~ nan1@I4_ZqZ8pנqt6ȑqaq3[o$ `f<D$7Ź?)~k-.kSBT\u L2C{AkVӹϢtjƽVwKj:dP)"^|[_=Y>6z΅aR9Q-$)].س?5%r~'M -pcu`KS6{=#.؆']TT,tƳH: 6;I4Şf_-(n7[8F77~ќUK@pGs]?e(뭐m |P ܁_6\# -Nܢf^hgᚅ85Ꮮ_Jy^h[ņמmr^2<_ jO6Zb~!_ hՃ͸lËNtrv#'XK,zqsp"F I/|>ќ&45^e!'X1:-.W*t1sx*)5iݗax}ha-3"!oU_=%P"<-w_=58KGGhtvZc5X?g5*tVKbM//ab?YgISh[NVW˸e *}]1~9%,5#g'OO߯ 狹a1Wakaos܍]U쌃P$TL.o#Te] WVޘuE qb%\uͻCw ִh >7D|Qxs#Xn7b?՞ 3بLql9"v>>(R\حtC #x}w_uoSJ-%jER6_(J!)׮gE.Ž&Rڛ >_"e;ncs` `hS/Z%Ɵ=#|E0O{ys{9I[g<<J@Z!ngWV/QX2Ȣx̽u Ťwg UP,'^$} i܅Wh*o`AqXˇhy yI_ yp35T=!4Z^<DkŋqGcezwWU~gϐIfItF_1i?!zB.M8N&71g~5{q^ժߡ̾߯TPMv>b-5T/i6 W4hK+-x0IT/2Y 1*L]'o>UGe\>KtfX2Ң[v1hnT|%KJi8R"x14kcFؓ8ڇJıQeH޹Ӄtq/ Q(c &%`ѨM:TKvsZ5ZLXS+$߿gWV ilXSa`aow]:M/qI\RuWG<'i,AK>?kG:ŸYul?|}+9x*!5z#2j ۾:$w,=hg9|4 uŅ4i@ 0FqӫΠv-Λ9 ;CBC/~:}~9*+< 1Fw珃+}{ܳxt/G݋`eۿ5#zҰ[֛oE`TX_֩jnV4[ -A vdto=BULٙqՠZo:&ƔV=g?޽u;jFoiAB_]:zAf%E!FR鷔2Bq56W ުU^]^nZSyAK<>PM{4xo5q{C)vCGouxhNkNv 3HstVuat;v$tMuGaipTe_{^[%{:>v筓x%ݑԁ7I1OBep/'&LB6A밈VTUsAb]n6eAp-A금SB8hUjUoag HN!8Xۃ[QseаmˑACR{DRVgUnrpt_}5*eC- s0#]8"8 V9Nw d3لJ] -/ euL|^vbR(٦ι(lp8R{^w3V=(F=E64Qݡ(k]dQ&޿U} ^+7LA _/o ~TTתT.Mo;=Mlpkum?UbO7Vwޯ90jt46[Zk;UfBN8a4/W3*j.;{S ;F!"Qwp%P(h]@bAz^w{Kha jaM}%9esƉa=@ .Gbs֣Vs1lQA{B(ǠӾ귡pG YWbyzP%7{hj!&q_ނUfeeЮ2:y=?w/Iq1u 1A3| 2Z.`rp10k=8sif`V/A@[%8]aq~SU+3{ G[0.0s:B{X򦥭,éƑwegi1; h9a .vm7#հƕƀj:z1?/GeWQ@PRO -h̜̼-I,8%%MQirMuk]+KMJ2݁ +Z;Z>vt/z~ M-] jSwᴓFpH%c ֡ r!NOݛuc۴L.wPQP$8>Ʈ=]-v)(lvTmQhb_DUP̕82CT(] \Gmb!u HE#7UCi7.ZMqzfV7i:vuGq o1Ri{1Eq V]Z0Js3!3~ďIOK{ 'UH*;Px0p߅/I^ d TƳ`y PA7^']0-{;(}_E[] 'R9**k0 BT|:wn=Va=u*wJ=sŀNYi2͂e߄#?j]kG"Z*ehjYh5hq$VFph"`*Fu̩:~F@/͒8CC?HR ޞ+D`X]~鉃3 ßQ,dDw$vG~2^W] i*[RIxRq2a[? ?$hyx>>iAOe J*8gJ%}H)no~83+v4C*6Λ><۱.*A唱cO6stڻvM ?9?q{Ѩqs_ LuP,^m]= %ab%No! 1{ǐ9gV?7V|`3aܭIʱ@15YWWemQ.UOU\TLDdjL^PqLڑǾ=V0#Ysj(*SRZ 9F=ˆo B!*qPGAs 9Հ @T|kRdnI\M8 ҉QYkhNHdQS xWO3? ϩQ[]+gj'Y8=At"}cDuXLua׵#Qq=#]E5H P6V1 h,p}-хQ:Z  5>֟[%,LYrTS] `ez]$`,sW tMM,'r,Bc'UƆʟιY/Ym%(5DgϓXroU׉J@TF!5I44Y(*@ ߭72ɩhkjuP3BZ6Dz;LdW.I"zGŁcp+!M(;YX g|"8heFy,:|1H%SB$ɤO`*x,:[Bl!W"h*dMtqؕ˕U[*(G1()rmt:N` e ,KW{4-ψsCkeZ\ȶ' O }Q1έaX 2sClWRR1u`̣t*S{ @5Yt s+ST`d, gV`fJOXOkiUDU &'tK甄!-SܖW)lꢗksm?m0JĢh"XS #xjG:kY ;?AJexN/罛,!L)֮E8#Y<|gcod,i* 'dҔRiM:IIC,BY% ఒ6-U!au˯ږI[~q[ #D$ I@gT?L~y~ߡCF(>(|kfk6 hAQ_Qe9Gu=K9YY Z9D͇̒pV%ύͰ { iE'7,cH_u93PȦ⹝qM,z#1 o|'b')\dI^c(KNLഖtZ_rnP/MW؎a ܊lgmfcڝެ}!1pU5c ܲ>r`.H LWuH_H\iZVU7ҽ>roS1i2 |kb?.C:y\ X"6M2k;beT֯nS`e nin<8wTO;zڅ{BS~ªzj>>:4Ty#<ҢΛ\UAlp@}a(_w)lksCK/M sK˻ -*g[/dRݴ/eS,q){mn|}|ZsyZát0|m{,b.c.Lh"M,Ts\.{(T"xc5m`oyWi^nb77C:[u=tه`axoJ"2e<[&f mڑ3Ϊ7딁=H[dII‘Rs&U`FE`))\!xoGQE ђ@o 3\z7`A?>VL6Vp]PXtJ{Ǘ B)o(w^M?w5h9A,De)VnԸ^wTVP&T6U_)p7BP& wTCܙcƄupds98$ 3$^5(U T]\0zԂGlBjgLm8U8JᄽxNQec۝^;xP\ i3И]:; 4' LTyLOL5م+κNvpy=R;ZbGyĿ#o69B㠨N8X,:@ Vu ={yIN\@Q4U=`xMw.%1tmJB0\$"a JZ5wάkU_֥QymJ̠W~z&uuShRI`s ) :/fO>_~A2q,hC6oU-DLN$L,i.?ª3m.h^"Nz8 agS2㳭k3ړO$䖀Hюy 6}DkۋF@:~e4}l;2 xwy{;yf#A<4g'v=yʍk yn_ۃ E[ `dyl6Kb@O^'Z)]7-`eRp͔ꬰK{!(jYZA W_g*G$&$-Ek @V5=|'6ϱѩaI5RKSFSS b""zN̔ N0K6g,q]d S5-͆T"߭B(*o_?4">,y8*P&?(Ҧ)I`;ybs 1cn2WIA'\ώ5(-XG"gIjqRI`&f lp ]Yl,N"W'-u1cE0@|U>sC#TN*iKsa+{Cߵcg"p']}j`nWnJܢH1^+ٞH١Y6p$<Pzev#%X'$k̮T9|fy> MU9=DW*uBAg}J"WV`RJMw'R~cĝ0Mܶ,VB^(ٖ00 @z1'g<+0BU"j:a5?7)M%fh`=߉vNiMU]tEb\X{f9p)cl*|dRADc7Qay\갲YyT,¯k7W!ɳ0I^q=\UO:{»R=H]bǽΝMdyP/U#[[ېw)}ȝx֏_xfaJ`ۡ,̋ay/ 2a VsLEٳ/V0vַ60ae뮝\Hg]LRlj-aě`ju4dOߴ(f{o.>À0s1}]kUWusT߮8@0J^{k.Ӈ[c8I@9p^ r:P\S^6 2Vz)xMO'{vh^xG7'0'uXDklq'+tFR +c鏱%ZWD“v~ASs†ρޡ|zdۃ.9ңw@㗵O.[|AGSl|f+HS8s1GG&[^xP F2kЏ&؊t e/60aA\ڶ6iF₊Ϭ炕mֶD4T2:+)=6yMjxUx=М̍Oh4螚wkMuQo7GMc+3"N?L^s8*]&@=Yc\9 2G뇅f^S3/kA7N0ϕ&$GU+qH<~Qwq=NInpVg$n6a~U~w|]`-XdpB==+Z9?2>=A5jE"ͣ5'\#JDqDVnF.#*!,u;ʣOшh7>nQy#=#,b >:x 1|YDy\h/[~ZɺZ 1m$f˯XZsYw, >w}$& Z10ʐ;zdN_~Mx`qF=96n]n\m4Enusn#NI]CvF*4̗t,7-jsVUdKD5CW˜Ra% N`ђA;h]v,%mE).Qtʸ PnSط]W ۵pYn/6l*Xj~wm'<,]amk&*ؐrxGTq豕j-OLh`o:F|2p9ik )Ut Q=ǠO(6DXW̙&}GqS/W#ъ` ==\6qئi!.y< hЩ"LOuŔw>Z<; _"$)lizRB=>յnPغ2RRxzI{ i["1;!Zg>4Y\֫nXG8#>$>r&!dZꛈ#R0X3fEx= w1 w궖j T:ZsPB'zcpD!+?ѶHKk?QF.7|p]A_^xGSd3c5aW}IֈjDdO<2" ;:ᆢ^u,f3G;AK;9E.w鼻0vU?CԨ>sC VYQps4I8Y/'r\4?H@wY̳ك4 ob4\) F VS=R]P)zbp#7W,w]]bЏF(MCF9L 2˯.Sn Z :AJZ+ղlar#̽zlt+/9eAyp@zdvD3Ip<1,Q)Yzn4cf_x͝G_v퀵zRߦ=c׋ng wq4]\|_D/Fkn SC"P:8O;ê4DLG4@l46ԭ-d]*F`ިGq$L'IRSnog뺷R}ybm, V0IElfu c7(8{,Ex0EMH*ZB(bi>J~2K~0tJlL-jLٕ Yk[RnQj6з%K 5|G4 W ޙ˽T0<0큥Y4) w:Ѡ? eT1WRLµ< z: ;D1R:K0 J9AW kG_>*^x7o$ pȩ =f7 ÷Q]gPd&K1_`Φb?fsg"N01Ek;Tg "Ymc `9ayD#8&&cB@cwO.J3:~ 34h[)\".^0L96vr1&(&1WJM 7ʰQ(1H_x*dw9`b8T%pȭݗ}Ui!U?@)Cr 7[ܞ mS[?aT ]x_ȁ zJ_#/$G7~I_+HqGС븡_'3 YbPmƳpQ[9'忊`b}ʁ7hE/^JB@E):ed+@<nhZvAz5GT wN;B4 k[<4FrFOAa}(c@!fڑ=CxAᩜ$B׆t90&am*cijOP!"Jt+[{ڳo6jؤܰviCJ>FOLk͆PJXM1ݢτ}[Vqk DeUód7h."t#t /ˠ+(8\9#+x4Y r8Panm?{X¥M&锑?QGroIKńw|@?6TI晳m:;ډpcbͽqC0N_4aM4Y6Gnc۱Sx/w+6o0) 5ɹq7.++Q%,0D-Y]ȦznjI>!2l>E6 j# D%/"ALygK1uOQr~Iݝ2֋ tuU&V8-p[HudS%VٍV[:ZH@O#0:z: ]L .p<ū5o%=_Ml/!>AO۠=!( ߸17eU)we60\ ZMzx=i,1*3m!%%ҢR!fsX@ %5hazAX]7){N:N =ьOQZosxW*% 5MM{*R>_Q`S-9qaYgԺU}Nibjqc]_8^R K`اJⰧlmOV]ŧ)9yww9) t\$< 6?‚r̎'J9# Nm iZY/N7d-<6gةdqC] H\>ĞT!!H p9 ˦2$[2`!R Z+85(Lˍ!# sO5k7{ep*և4,DžZhmꢿ6.-f526r;cg F2|Vdxui] gZv41TfծZ>'3 3:ºO%`/B[i?HrwvG Q(.=x\Z/M^ؚ@7KՃ_+Ifb!lRkAQa矐v>?x?zXn0xG!KƳc[dͻE*A:Qg ?,0 |ή`>$g):ͷwH 0 e U$ p+"c.ҕY)&?{0᫹q*߬V.aa||օVxwNgPsPzLm<` è ]N6ǖU5נdz%ʌ&(olDOkET&'ܵlS$%%*D;!7BU`xE?1ӿYlzQ]ܤ߄, ir#m |ar 'cT*WxI}U?wi5Ŝ?THCBR?̦ׅO) xO?ÖJzUXTɢWJ[]3b(Z{'64LB7#?4@#h{h5}*i8fX!:vB5RPD"9hUl ٌ"vRQp T9Mvc:,YXh*[ sP0| 0nMj4:xuRn0V M.\AbCH ʱ'ͨF{'-H<N4[ԓ=}4{ɷ>\~ޟfNNˈ-)si+Љ|]T UͫjP\9kٻ }@ |P@keV b2F"qLe[}9.2*[9ECCfrI@KyڠvlԨs}$Cq )c‰C@$ 5Ǚ%z9U(vҳ8ǃ'bY%1,U@Ů/ UI)jr9>RSLSXBry1aG_, I۱'Ldq@~$\=̞s/fLdde\Bt xZ?ՏQ`2 _jf2,xWmo6l[( %m2Pt@KMD5Rr5#%Y/N|("uw|8 0_ϒ+'}Y"eI9ÙL6J,W)L!=3b4TȘcE̵[D*XLW\1 CI sZs VBCR(<0ϓQ⍈!F%X)#Ll"GH " 2(B"5l<ەV::UbecN +OḰ:Jt"$y1tfCd6La:;:._|NੵD^S/ieNoдҰ1i1˶NYJ8 5 P,Vgƫ^ }t6Ɔb Éѵn]Z q Tw}i!skZsK -IC#EdܺkL)JX>.8ZrttO3ô+VtȞ1`[[fݯ5/{o02<&3m>eBm܄-!'m%Xg Ȕ.  $KRPٕl,aqW!9miI8_aߊCX\'o)د2LZeT8`)"Cw_? ao]Ҳ|JKCLJ[$/ G U.j <>4i5Y"ʱ" néJ؋Dxx!YSYI>tXdhԓ1ޜO(֢ʱk[-KD6GZaV5Kɫ`i|~^)Պ*@>ɹ*n&' Xcܳ&o87||_TbhOEz:xA&Kҩxgb>Ý\ogWoZo/Ec`v & ={*4weBWN G3NIGhdƈΝ^NޞFv> } ˡMF٭"(d?pz5GP2 -#SgnE#ji"@|ƣ\Y *;[3z RTb'`Qvx5>S/]ʁ+hpy,Px.z}+uj!5;X;E/o"m_x340031Q,(*-)+.HMf:8ln\ӝW=;f B8㽷 x\sF9+|seIsR+[vXl $V @c= @ҁ쭺bK$0ӏ3jwcfijEVgߨō.y׷TEItiF+}uYӋfYZj?ڻЙ =0;Oc~.z}.LWe~Ey5|N(yRǘzZVx84E]ƚ[\WuYKǕI2ܻG =o}->0e_-~{^YrQձj^Gi)JfoDL,S@EiU1WSUUG ,9QGeTu=WTD@ot,ԃ MDU/(K{|I I~yM˙ϼhwK^eAxYhcA$ Iyxޮpy[*=3 kSt6lhs zy7Y}Mt[r|藺5ry&{;y%:8~a<~a@ wԫZ/yӊ+K SL%+WjPλty'o^/^>y $пO./|bgL+=S'j2_uAfF!ۿ&촟Vq"X2KWiM&)sCeMy|쏟De)1:eP<=L#)뼪CjI|:b2D%2ܥ&ejZ|RƵe2B1A!@:Vok5:qn|Ԭ^05|6CC@~g k4}<hg~~qG 8o{OO8aVќ &fy愺., +N8Lb(ݤRV3}0eI$lU*"._gQ:UV2qj &Ki*A&y&]b\o0 |Ɛtw ME+19]^ 1"1:6wZ v;LN^굕I# _vJˠYEckLm``땶QyVU@p&Vɋiw{ߪ_iD%Kg$wQfEE–P@WԿEevS>R>-Bۨ;!%شY=yV@X]5ޔ(iNUIl i<iUY&d>I"7VtV$w}ϯE> =#&fGq gNTdV+MdHZ(Kxg]FF^)kD8dȣkZe ;"_$d$fk/7 ֣.NSCtgLn`Bœ^ Ie >N 9ub,;F *O纱 UYU Cun}5pKz xTŬ(ڸs}axrXv͂v:*:"FuYlDv<>$ I:{o2ʴ$3GsPRWZ v6g >ף&RN+ZFFE R5v~{*,VޖXj79yT&Z밝N͉M$MJ)Oҵ3;HC!{x nCv=:Oi3Rv*5B]M~eɘoNC_?tƄ3%le#l 2g:u[zO/N(& {H1nq3u " Q36,[66 :`: 3=Y2'4Y M^\G*5Ws n,|be~N! &&un)uQ&#WwvǪbKGFCrkO[6^sĿy rk6ZXOB;8QXQx+ULSd$kbyzNɥ` a# TLq/iO/&&ϊN“gʑNSD[1V@v  Q,%pM쳙H&5WtoSŕ:~*jvy "v\2Zt#5(TfY DC[/(_{㩧2SB$o&iOgԲF2m73gAںbU+-UAPl`bDݥH1K8R1-ޒd Vq3j9g]M4_}H#rCjUa3%K+F&zM;7זW]xU!1O E>]Z-rrWZPm{mFHI8&zk7/[WwԔS=WYWM{K[W͊{1CF7$oeQ |$V+zt-:->CFD t5Y73AO'-_d nGrsA(l^ރ]mʂ ɥLgͽa*4psB.yI,SS nH SPƦ8zz9g362 .hEJ6=OV44ndZR=I|,=D#y&7QA4p.yqWϊy¾aھjZ*̉+\m'g|G.+U^7fc{L 4}¸+qR./I1'́6XF;jKv'waO\~@k]וT/h)m3 l vrtHhAaoU"Q= ӥ@P :zjRK54ӱogO 8";|tͱE3}+'u&2S׆6 ֓wuGT1msϻw([2&ˋ fz{" F?< xsqZVP"A fgI+b?D0odEΐD?PK l+ X>w`g_D|kkoqJRdf]ml8CҫOwf6i2nW>g<4^ l &|J\r#l I.@EBN[1 yL{M(0Y[{زnj>GU(nTҘ?a#NcF/dt~qj$hԽuOxCޕ p4N a͹F82&+|M; 1`z'5*" QWE>a'OD +2> I62 |X=䇹dUۉr{įh ewCu8 k}[/ρmmu!: L".vt߁V>GPaG̓0\N9Y!+Av3kcnzYCtQޡib +q7"w[nsieG+!"4Ό [3*B `چzO=ŢM Q[.h5hWĔXeML.Gfc'Q`B(-N'L8'82zoYNT*Jgۺ*$A i⾻6`<8?L8 [tgw[ 2A<=.;%jM_C\OM@pLs0:D$>C^mɂ&;cӲa&d]Owi₳nQOvGH`Ɂ>{HL,#UVYj<+zyFp ,f~Q.|'EZ)Zss Kqg6}ϐ73uMW:9Bvr±ٽfIfϏ;2FS$`+Ǎ^NOhBrV^ 9B=Zn8w%юtA 2})zvl YPro$GSM2 TFq;y2ad\hT^PD>k \ *kBNyPVFl'dpߝ̎ ~\F57Auvf#5+dkpV9<[ mxŶ3,gEHqM6Bti OlXt=44݀|5xpW_xq~#ZX <a**e5GYZpMI~\Fi%RVQt847k"QW4}ƀEA*jX]i)AP, k0ܟmk.(Q| z$7oɓP^m9q Bʙwös? oi ӓ{@C{2qWFaM?)#(a40o_8gW@"F_@Zklom:I_NTja?n%OmÐ;tڃS h*pHXWa/'8w&4D&#ey3u6&..H{XV`"xeXa8qT#^4gpn7:qhQX3:ضv+y)ɝ%CvtꪘȩMjP-џqs9$ƹ jw6]uN+5/xAz#i_؛ ry(8v-h^9ljz]8;?vwSQPΌ+j^ W89mɦ,-tqrk AmQ쓬g%` 6}U?_M/a߿+7@qFQ^EƥJ)嶤R^I̻NU%cӔ-ll:+io42L΂-cAlt\>.*N#wC8M˱np-+be>O}sM_qiml^ cRAIE!HX\g[[D>\~b?^k /r*[h oӵ(}M'eDf;DEz}ugqy/ UOV#\nӕFmmkf s5ͭlK%Ҥ PwBqe9<;#Z>@;̩ @dA"1sሇ }F,0ܯ+}5jVWC| !=e!oWw"Fƈp1y+dvrz*=iHcCQo!/2zϩ hmоw6hl͵6Ry̛:wҕ>#g=@ce஺)io.< [mf;=`Rï5<9;|Q!I]n. I0}[}^'m~ChN&Z΄:8;!-P@FzN'){兰SY4ѩ6qQԶ\TuknM7.֟SG}<SHd %@fU+<<-8^''4tlmQiؚOXyyLTk#ϽX.>J l-(Shi_pM0z:y<[-xm[k@@"Hi7` 6.*jɿjbb./9`,͎ ]_Hrip KD՟Y>`BG MNZq nKj/퀃 M2̎`e$ű̉B(1>&PzJoUȩY$aSc H/AtI:0B*rNEŮ)qv}-!8iΪjm| C Sҷ-2=[n39CcԘL詧r=gkvxQ*Tm4wVҸoVv_vOwm_*ݹo:(֊sxzuF)aF[#.πx7ψx0W[#@c!\1Vñ(3G\G`s%c#{Rx;zu$F)aF!c.πx7ψx0W[C^y F: FƛAx;zu#d{FY$s#C͙I*x;zu. g@PgDkf{nFvxB:pw`fǂ`j100644 version.mkD%KX|o& _kj>x340031Q,(*-)+.HMfhytոDXŽ-R\x{qZl [ι9K(ih&xr3PO磲O#}> 5G*7>m~|0dve3/8RMӓZ8$h  1;Fȫ100644 iprlib.h'ir&>7>BlE@fxk<"AQ\ASH?3/94%U?/8x8T)I=4#T,ҫ .4d; R+OKptC33ts@J A&O`SWqJR 8 @\Pb' V(H,).M*.Ё8_GE<3'%9(EAKBS hEq46@ ,A$e G^>gQ.(Ĵ@{T ZZ+E@dW'Om83Ksr2 22K&2`16s0|78x;"6ClC*F.Ǹe rtuSsU㋋")E 'ksK&Vxkҷe=͟+ӊs2,yj|n~>~{vWEWM9yW_e4"!8hxB:{bq|*7",8p_100644 version.mkB ,d#:vH_o}G٩x340031Q,(*-)+.HMf)tʿiWivm\ Djx{a ,z97WrDl Jx;zu#d{FY$C#C͙ISxzuCHAlofp+91`  xkeøׂ%SMP xviW&3hMݛ5G)7p]`#?=iFu|)f8<| Ywœ .100644 iprlib.hymۢQՋjB4{x{W_Ɇ-Ӄ-z [4/cݾͭh0xk'ˆ9:'7ϓ\8)~* uexB:#)IYlm ;U100644 version.mkXXJgXzkئsjx340031Q,(*-)+.HMf8;̎|N- ;e*(ox{a ,zf97WrDlOx;zu#d{FY62ɌaMxzuŊ;ݹ6\rL xq oC c vo3smgfR Uxzu;ǜ/W[2o}|4.FD/x>  ];T   @f>t`zfOxzu;ȿ9/>.(}:3Ҍ& %x{WoɆ-L۵oNboeb& fn^$_W``P\^%}Mfl"|p \\`QEp 6V3qv  jjHMEzzgIrY11''L(5iu#<{ۗn_֙2O}{v mUli,IL{xx!gjz"}WÐ5P5%]Jx{re/FAc#-C#(YBYVxzu;$g NgO,͸v Mxv<֓$b##ׇ$(p-?Nv . 3.array_id;  Dpdco-2xB:wֶ?ΰ[\xXM+100644 version.mkeX"Ȗ?F?O>D7.x340031Q,(*-)+.HMf(ʗOߓX5K; x{! ,z&9BJSJR, '_a4ݬo]N:xq! aK9Yr 7_l}l 6&x;zu#d{FyɌRS*xD 8L펉^v&w[5TaM100644 iprlib.hqP :TI^K(BB^xN {&oS%`S rx!Ԭl6BWtރsRtr,xQr{Ɇ-LoN{Ǥ?*+wLAYGgZx!ozvɫ|"(* BN ix^r{ &oQ>LV5yUe ''M~kQ 'ON@y{y˯ΰnvł >nN~r|NjLnYg]>cxzu;mW9}2d$lҌU :x[^;Ɇ)'0Qlz &i&k镌zbZiE%y 1@L@@ĬEfA~!x] UL.3!{8䫄p^!mWVz100644 iprlib.hUvu#"|;m]TUcBf'IxNrk '_:b _w|&o3dN_,>]YjLCAb"yŦcNsEYjj/"}>sLkfr&ۧX33o?e\4=сYAvz s`YGgY<ۦ9=):8S,ӽLed/醯Yķ7f9'YPZ<9BEa.szgX"[ooY%k͐px[Z+Ɇ-LmәytOn=bbN{EL7'?9>'5o|gYhMĢ3]\'|dEz:'Y?Y*R_-Nx;uqކ-L:I:Lۓt3wpx{eN L~s3ۇh Axzu;{Ň39ޭf^7X1z4.FK kx{J+Ɇ-,KM>1 6St,%OOgA{{YUgY_.n:zxB:pgK`5?100644 version.mkE|eCɍ\b~>Rx340031Q,(*-)+.HMf^}rњ0}% ^\7x9-`e[[54h!nœ-į'n+qp HxB:4I3ߐ#fCZCo`X100644 version.mkKŷXXv(I*x340031Q,(*-)+.HMfw1fUKt_ _n%z=n-xq! a2nx;zu#f{F%F~xB:A[f,SzǬax100644 version.mk,mOɚ?-`}x340031Q,(*-)+.HMfhc2WcH^f63 Jcxqq! 8|+ '_4d4% gSx;zuC #obd FAKxH;T t7100644 iprconfig.h 489ӂc<عzٳB%r sx[ZdFv&KvvF fbf fh.MYL۳|y,̬8 58uz!&^lrEt؂,L%s2YXNO0cӛڅg'Hĉdn3\ s?`ӄ{`RbVaa,‚MؚkoK6bl3lsR5m xq f&<4NJ%x9 Llb8>CNtMyؐճrP]L;Z&ڟxx)ydC się٦3md51I558$ &0)g01316Y)Xx;uwqކ-{9mL 2n  ANx`;$^Ll$-Qqz100644 iprconfig.hȘ};/ocμ MfhlB^~IJrj$tDlB%Jux;TrCɆ-Vm@YJɲvzé¬'1n7cd^)@xqy ,6{> [Z !x{{'ˆe7-sfηz v|x!A%Y@ݡ#q\(2x;TMɆ-u|71v;3nwlgf{ st,& ̚`3-lhSmYD7'dWirUsqf(dN(&7tVx?xǒ8/v,Ә "1gWݲA܇ӯJI+˄¹LBeFh0i100644 iprconfig.hߓ/,0 ;:ɓBMdmޒzXzēJ*GxpSɆ-,-ۥZXy8 3MACcF+faE[ #M~Y.Xv ,WT.19QF/N/? , ӯ_eQ/Q.H,JUҴ8yPJ,,A.#.fMcgqT_`c޸Enzu Y Z\dw hl#,/pMϹ$J.h$YRsS)ht%6!Y Pv+0kULȨ,LNQp,zv|v}.xqy Jl FZwo}rs# x, 1+-, 8@I8*H*Hv  xD 8Vڞ2HDU ~Ո100644 iprlib.hLKY+tzl"WRB\x;pwކ-L=|;cuMMxFt! #d; u8 reserved5/_75?/NCLx  ϣnʹybˁWWrt_j6x;p~ކ-L=|w1ʏn{?F$IoexB:qjPBچQiM/nk}100644 version.mkwEt! glwMx340031Q,(*-)+.HMf8´EOUFU|' ^xqq aË9]R L &a24 Yx;zu#d{FYd#C͙IOaxzu;YSK*;&ϩfi] Zx ɗ11@>xzu;H#˪ knβ?lHuMҌ ]xwIɆ-&ŕ9>AA: J@nI|fjB^j91yJ: : % Xsqf(%[sqM枯li= nS$ڏbbf f~l}-.xwAdFIF6N4龌 3 Ydd;&+xzu;@3 3yIÔf.Ҍ .x  b9 `wMڹ) “_ i=x~ gN%9Yg4L!69n;B^jtGf 'KgT7KM":ff:{  xzu;HD7.>$;Ҍ $xwArY IKrR4JR458934s3J4'U-VQ(N-.(<'5PqjNqE&3Fq:BX/12nȴ<~f>0M`tz&Y9Yt 5~e2o@̲]+'XŶgdEDtӿd~kW&SGVxBb) {  Vjfb^x:3rvu χRm[q!d.ݓBJ:x{6 g:&Pd4x lxzu.;DO{Z8kBrL Hxj̿ ɿ 'Wk@,mN' $P8.else ioa->is_secondary = 0;GGWHxzuGx;/uBrL&x;wކ-u;3cٞћqgC'j`b̻%kxB:|5tD;V<.100644 version.mkT'So2BԖr6x340031Q,(*-)+.HMfE >n$ 7@Uxq^ aF97hV`M9x;zu#d{FYS#CIANxzul&WG3 &Tۢez&5 x340031Q,(*-)+.HMfQQ_Tկ =n+x{^ 87_ ;Ix qs$Ħ>v~.D_x;q a87rՆPS:x;zu#d{FNC##C͙ImAѥ-x340031Qtv vehW}~u!DobvjZfN*Cw/$X \P5A. 瞿v5.Us(&3щ~A/z;7HUge0^?Kd珥: "H/8et..]&t^Pg+n` x;txc(Kfxnw~edeWuNc(`xfFsg8~R}~ $*OP-~n5ԔŰo#/+{s-_NQQ7}h_wƅ_AAqAj2sem<|ιBM)K-*fuo4xn=`x^udI!KvݢɊB,))@ +_xMQkHSqn*c>*;SMCX "̡91@zHZd69)"M^J7]95´>)# DO(Dr_f7/ a\H|^S.z][ˉI>~N u8uΣ_,CG}$J̭7Z_,j;׋kP]'U^˘-lU|JiW> ~6-%1#\.DJÖTΉ6UV;]nsUb3Ƚ=+ғL*`|*nrlT*>S>Ϋ9X(U!I)̹FPR[h䬡BHf[ Q]PS}%MDPQc8A 'C r(#8Qk4`RғQPu6GpusTPO2 t?2Uv5m9rmwƓҌI;,2[ aȠPduu!AHkw$P-@IGBegBp'$&@h38 SN)0qdyt 3vz l\?3hw5$h*$Cl2s Mx5s'U (512)"IQl0Q6N|e>V#tx; a87 lS=ox;zu#d{FYǂ"##C͙IPOx:uC.Q،X~Op6_Bn~BYxzVq-LAϘb*?K;k& fE7adnwU:3&z1=Ղqz#V2 Mj6=@u+<ۘ"DC c S <-,& 55\ Y/lfmhaٞºx.@}xzu;ϯT$],7y.=2准Ҍ (lx{Xa c' lY[7F fbf fٮ6å-x340031Qtv vehW}~u!DobvjZfN*Cw/$X \P5A. 瞿v5.Us(&3щ~A/z;7HUge0^?Kd珥: "H/8et..]&t^Pg+n` x;txc(KfPyQcÙDĎ|;Y kf4wVc-ǭBYJȨ߂1ͼVZ IMinY ;43OBqBQNfP 8I%N}Q֮BU|[M;M&fV)C)-HI,Il\~,sQCY2O=f.W]qydP\ -9'>MXbbgf3Lk!r[B,uFvMq|a'JCxz\b eV<|2wINcoKe|~̙W2c-JVU,(/.I,*`B8dz:c0%?3J2No bΨ(>=OQez:  ꌪӽ;j0N$6AqNuZ@[XGMb®PS9=hzw+K6m,nnQ?RR˦sgI,B\\i   @~:#$5P=1HDkzIf,>oxw"ކ-*1UnxeZ F7-œeduh/L@̼|XEG8ʉf&e$g&)*Zowd%3Sx۲O^-œOfLn<]RQc2#313l#xw"ކ-L.O}Lz y x F7<,s,i 5;xzu[_z訮Đ{9rL۬ qx[\ކ-&m2iz F髦2*Moəj``həSQ s+䗖XOθ$֡5=dҏ `1ekf2nɴ~;+ixzuMV#(X.frLQ .x1w\ ς'k''6;;0nwa \x!!Um⑔u>\n!ș x]ciCd1 ~5894G\)_a100644 iprlib.hIp?`bwbTy[B`(tx۲O*-L6?8d?ӓ~2 2b.yY4kg5}f#,-d޷uY-&@0x2wLކ-ץ2 O2=?lF-L;utWbxB/au~˽Փξ_ fabric[1]^c,!Zx!&AޓwZ(fNxJ ȵY;X'tIyL Y?LSlgaU7 } L~kϲ_D0UǤ5yM Ec1nq㏳Kְ*11ncbZ:uȘ`0,Uaze&3ہVFOĦzgzB+w3Ϙ(sqrre敤i$((旤*iZRsS(cnArzY8&3ISжUO2%lȷp @{]ki:xzu;ȵn=rGfi]^xD|-LOfLtq{l&U2N;In+L;gU^ e{"V2=xzu;Ȝ2ܧ3RosM{Ҍ%>x6_<-L_O_Tɪ0]遧Y%=ͪ8]!̂f>Ēm Ȳtٌ"ןbMv1Qx!}y2i Ny -x4Ob\ 97cnX~y;N.NΌ YDjjJ* Rlm5y񹉙y 7gy^"VG$88 "VӚkzSYQ-}xzu;H}IoSu>EKxo,͸X o 2x1WTDC ͓wa~e-%˯c☮x[^.!AI nw@p} V=pBz:pۅAr!.CSA4햠C˅2i9prN: d d&FIcF+j6{%яu\lDdcd1YP1eW~"]khh NHU\*=&g1ߴStP^DG_;-:}Oxzu)K+1v\9L&^{N?Iw)^63M'вS-bkx;Mo얏N ҁnojwƂT]2'ElTHJ 8sNaޏn;AZ@j-h[VUn:n Yw.-$7Wk)QK `~#B /Z o p#~fRu*;z@. ƕJ췸 #oUZe&4k\ )GqG _RC‰q/KO&`" ߩ0.ε(lS,cJ$ э;Y~ !  DJKoӫ>sVnHގy Q,oEېb/]1Gï>ެ6WċJ^Q;5'D %}rW+Xx A׭(`ȠJ~8!98H yoqԱT84K8.K J~r=`va&e6';\E*@X$܃]:6y9'IT $Ja%dW_c!I\Vթݗ[}lOHA4B'zXi0DGa_o6wW7ͥ16t b`fOW8AQ㨎K#riV=e{=T8~xMX_3-V'rm,N‪q#+S.!P@$m \|nroH+| p[mњZ-\{'SbVBֹ٦m #s>h?TXEv]&]n- '%E>uU(4e;my2/l[HU vT؝JTMQHxovZnf(sfRـ'I𨓪f9`>iO+W91@bџ4 < J2됗T=(^&g~su)=,%0]i,ޡtÃCQ$z SatϾ Dd B*9PԸ)5 d20tSK-,5Y3R%C4(!ۋ,~ hyNA"&\Ӥ!E==-Fnf*1T+jfn.2" hP09f08TUl\5j1A׶R1Db< f+30kRRuy`Yql#>^Nl'`"lbbTc-` L4jGZ< :K8 yImސtgVBhivj9ibqѧgyJhQ3qckPc ";ˣ(>&pr^ntZv*Pk?T23L `p$Jtբw+SV8RFՖ@rBE58S8~qz5?HsۯfI#jI]Zs{ .]Zo9oiYKI=0QAV KFVpǶ>\(p:5$deKȒ@>MB=Ab#Zi~gCMaO(c?Z{b:Cm8K`3SFmjRSwt %L;f&8!RtMشG,QHH`A>b8+ه ~ry[byU$!̧liM|nR|x|\eߒJAZ_TVŃ}cӠM5,1SA(̡`ʮ#nt Y큫hhO+(6ߺ&=;< @7;vb[WfE|)|?IY{A{75[jiir D$l!:gxt#$dp bIMD-BQ/Ʋ^) rGAV֤*O_WG 7s Lpџju6Гd̯*rZf P˞wI' 2p'匷j|#[dȀ_V 6TS7{OtՊr9w!"LT~aPrx`Iu=Mo+Kh) c.mPЛ֔ _E(?7FM&~K!~'M8ZxۡLoSADW@r\ABJjYfrBJQfYjBq~iQrW&PIu"``Ob=9L F+͇ <ABL`PzuDF6. }x 0DF,k$OVB,'gl; )0WݲVS$VD%ֆO1Ւ7:Ȉ܀,'2nv* ÔKAsxj.!M2?LST;P4S:Aá+i,k()ZxU hȢb3 =95P)ň19sVLa *4SYCY#<x„f0E]8x]v8)F:!?[+`GR XZT:\ASIzXP$cEoB Hihmw7 B^)($OHfLy)!%x\DHSHQEM'u{xOhJK&S[ XIgYsKdg6FԩcJ߯O[Wʊ65KiECK=K^z43~7L 1D ~gd KDsR2'w)'&> 8yVC")c {!8[D 0x ɺd, .>:a2p[fQtɍt4ۄk!|Lx&_B4t "U[ÍwpoQBj E,Z V h!Rä)R#rOO$J+/rJ. | 1 wa HF}P);65݇ڷ2AqcH.x3x !]1024]`y8UtSI? -BOLxB:ZӦ3;%]6tH100644 version.mkR|IehʰM9x340031Q,(*-)+.HMf|T1skߙژ:Nx;q: 87OW*/x;zu-d;F92##CIOx9W0_'Q7IR7U&d&sxۺT-,Y013ogr?33MAC0%$Cki,b&\'2I{Lg15-x»wɊ!zj'meAF gfL%)`᩟Ҷ{1~b4#)pNFfMόdT5]!IzGGQL-xzu;HT*?K=?mfi]ϲ MxuSmHQ7LfNjieZ-AAFJ_kR*DRB}PI]%O$A ,EaB(hysνCu:ŲCi]֘*oi}W;1w'C=ͅ+9;G2wB JBS1oV#*iAu ڭ*BG׃^48g)/ztyD5ȷϽ>M XYtXo=|Cvpr&ll>ToR^T0^͂X 41/2sg3@lƢ#@Sαz`B|-g ƞbflpc: "Yr=8lJ7M`Lix_6d*3Yy7z[am>wYMåȲߍf^T;*D!6-RŽ?,C_⦙3nU5E$wˤsui=[N'/vL`.n 08-Ԕ*wkGx(.uݱY{* ?x3 w&Ϲűy6=Y! jx!H7xd~>F.x?[m;&\U (G(G&"Xz"100644 iprlib.hń|GkJJr;⢢BQ*. 6x;2St-[h=$q+moJeδQmzl tݕ2=d3N(<&ktFiZԜTjͧ)`4/1=6 ,/)"2O2? 320 2A33064~L2sx2ê h)*hk&h*@ybk"m 7gLzv}Iաgx]L:}hki:<3]ړ8f%e1E{SdD100644 iprlib.h]I I\DÓBD'~bx;gW 4m?˸,\d 6]xeb9 7mej 1x9}vƶeH-T3864k10= #xa՗details - get vseta;}7Åq%*W0 9%hUUUUUr1g? ghc Jx8 ̃ͷ3vset and%P *.8smx]2ghoTVcy6yP8q6,?100644 iprlib.hZ 4!E7B_&`x&yxư  GO`)<ɗS.\,ہLS^ v!+de;36sQbtI* YY8 M*G"kI?N~Et:V%- Yxc ;9:9U9+9vsetPo:-K  : / p :> ::+;ͷ3vset and%P *O ARRAY_SIZE( ))q  -:K4+xg-L<]<}B}q3I0maީ s7x{reCޛɱ79&ۿa&HW |xB:u}#Z!ɷ100644 version.mk 03bO#P-5١g"56x340031Q,(*-)+.HMf9qqCvKխ3G>[)h83z\x;kPFӍ~SDҼN=x;zu-#d{FY<C3#CIMQx9'z+uu;Čn3(x&E4100644 iprlib.h! ,lX`IГB$\x;-Ε]:=QwzL,y&^Qݮ_8}Y?h2mSøy{o9mRv: j)evE))E: u2J3M͸jzyc$a r'' r2Jp?gEst '?IJDF3FM"Lf: MXe5x2eÏ{ޕ,zK Z LxB:G.7sИtW2E#100644 version.mk&-f!<Gy_>x340031Q,(*-)+.HMf8Rt(z V!\WOv;.y[+x; 87=WXq x;zu-#d{FY"s#CIMRxzuFQȐg_s׼nFUF6 "lx{re0fc&}"h)xv iDD~>,a| YT|fWZ82[oI100644 iprlib.h*(/- 'Yt B03\xmmHQ9ojs:,ʗub%56s(3K_rSЫhB/Y>DBF2 ¢h=9kt6krZrwWxVطNa˝ڊA/bVB)X8mwXծ*I6F@T!%m<\Am U1,NjFo:1"WX,Lt#txuGЕ j $| * 7߄xZ2p3CۘÑ}D:_c~ލ, lWfb2RNM_8f)##YB0*[~ S6Vn` |?Jgq†j,&x[+~G|Nּ;%458'tRQK0jNN̼y#313lgźMx#~Gi &Ԓ󶀙whMcپ7uf<@xg ֌ݭ>nij] IxreCrsqeGyAGDwe75 M>ǔ'Ty'/hVzU_YA} ibۋ T.fx;re÷DԒͿ7Y|#< "6&Uxvju.jOYh 1 YT|fWZ8W"r[O[ɦz"100644 iprlib.hsis64) cdb[3] = 0x80륓`0Ϸ0qVarraym%!%x:/ u%d&d&&&x:+ Bj"7A}5<L[xbQrcB0xreCrsqeGyAVz ĂhXk.R Ē̪TxyQf H%H=Dr~nA^e%%E/`/MlC{qKpg~xB:w^/FlA(R100644 version.mk3k܀pjoDeaSWx340031Q,(*-)+.HMfקv˭|_:-x340031Qtv vehW}~u!DobvjZfN*Cw/$X \P5A. rfכǤ;rx-rS&@T6G'O U"YzUCpGO,?^|z_3Ԋ̂"t2g4sw=hym6BIr~^ZfCRͫ.Y$Z[፡,A̅J67Su NS>,=͝U'&}KqkP2 ?AsE Gl3ohBRS[tÎ9ϵ:EhxD#CI>b~!\vzZ8;"pPY0Okv/}df>K eJ4E 7m#BMiAJbI*Md_|]Tfhʒ|o6t ΃$deqG.>dPSR3rgϹóFo*L-.xR]HSawb6G?&hfgJ?"%CZ[!γ+^YچQE]Utd.>xy{obxݥgzK$b^c;u̦s>V\F4Pm/+aԛLϓPI&/QW6EMJft#-ԣijk^oE Q7&)fl{mLs^? 1Tw( qk |7K5Q.w^!+ avJHDrV"/*}ۭ6C708ƶVНؐKx7^ۋA.0,B  i8CV m&Ho`4+6,>5߇p.psQpY2wFC&~ܘs5V^\֌pH=B_VlEtZo U8hI)CAԿU! LМ!al/u%=#Xѱlʢw?lIQ,(l-=b4.RXdb%mjNIO(N ~=V?_dxI \H⡓˷H&t =!k 3.3.Y1CXx# K h<~e.vKxocjbِԱIDZ7"# `PxzuȥlKʱLsf>mdd9 }x]}ѫeFc:(8D'b G㳷100644 iprlib.h8\UjwՂo=ZxBL)xiif (modeISABLED)>v G/* Default or Disabl#S(>$?x, _ && `Ia"adaÉB?qx74? q F!dis!SWIyWFx 2Mfܶ B)nK  xzuŤ5ZBu/x-?ىi2 -xzuBS+|8YDLQyc /xti:D-U0*C`,lƨӳ_xB:Vg$7J WXAMDN&S 100644 version.mk7rЩwPS(Qh\x340031Q,(*-)+.HMf^w_y&&8N33>:x;qB aÃ87_H~x;zu-#d;F%F͉@xbCxzu˶bIj^Fiv J!xx6ODF텉匓9:}\&Ɍed.oR 3m{f:t&c=x  T15od㕒 |xOF$ҜTx0oL2eEL Q ]_xx-nq7(3f>;Ai SKx? 6l uiWA 9x89@_C,z 7u,\: nB!{ m%˖.Ntux{IZ4$UAK8D@A=5/(3p,RpjNrIf~D-2J\Ɠ$M&^aU22~*(q)(e+gd(Lv, +(9%TV*OgXAi3F] hL̓ \2 r+R2ĒbD]RsR1ā D&++39] Ʌ& W*M~.6CqQFID&+e&+l^>n4X6J_$9?d-x{4iC(jjjjlv&xiC(&2k7۳OdgTE}$Ӊ*%ԀT?Oʸ~ᙞ kOFt*6];g>-> "&Gޠ4PH|*΃ \%1ǥIV+uw䇭v~[.2q7Hb$$ߧw] :ϙ2?N/Jb\ ,QB4KJóaDI'I ?3XQ.N yNtpM7V0r+l b ]d/χi$fi6-K%(4hA>a~1hvƟh(K6X[VXin [_ |b>vS3n-b{R2TmKeS+Ujw?יkpM|8X8 4 QimʵO637 [>FS#E~B7PFkVNs@13[`-MރNh!19בDLҡG!FN=2M+p(Fkųk@9mK1UB5?lq݁Zz9YNsX< r w߂+oGr5K{_'[#Rix{;9ZLFi]d20\ʪe4dC; `ɬe29]vMF!Fu ũEeɩ Eɩ)Ez:ӍLkY@ecMd&weN94) 63(XAX]$_!9?$3cY`hqBIFB^inRjQ dIsps;9}~` 4%E O2Z:Ysp(M]=(543'eˆyvyx&WZIL)*7ydiɍ2{&S|npɝ!pe,P 6hd = 4'on d>ixuSAOAN!Ը 8 (i"EJ)D"ٲS:)6H^băg<ݣcb|m0zڷo{|'+C#&<] X :UMJRT e!kbCƋ@"CDfH5jJDJM¦GD3YU77VJ\VRkT ]=M, ꡯ3UoxQYFѢNCL#:x_ b-bnxDe(AB={liO#T5j$cf 4w.ZI1|[b|b1b$s!8NO=~#P1Scڣ,ė+Y@}u h(8JtmtɘCJ\&]>A oq 5Wp"M+ Sdah+R$۹x|xk_WK^viiT.SBM=,Oޑ"LlCx; )wRa}>|ހgqH.I-91s4/[.SVswx ՑS>ͣYػ 8x]P=KA%m50M\,Dr *rw[ػ h^lwYf߼73~?k_OF QJMB'E:Z\6VNݷzu/a,Rn5a*ș2אܶ`ȓԈ⁘iXdLRzt7K!"9$ǀt9!ܼ!9Jm # >q4,FrxC_yf3p8U&$ I^whv;V"!^̧\ekom"r 4*-//p !肉 UbHfTY _+iN};}CDx}[87|5e[8 n1 w1v|%K Jƴٟ63gfgW+n_I y;sϣWä\Lq Oqg ?O ml7ոz6IWWW[ 3C$/yy=OPo|1V;^2e|n !6'Y?f'sN|vGUx5;i3ȓ8o=(td:x:ϓ ~jۣsMlgǵ= qGL!L`X22ӧgq`2 aLpQ`"3^.wן̋{A6_tpO`NF,=g؟I/_~stz=؇W}8=O/yefO|}#Cmсiorqwc{,=?Y*i@dн~wO\'Gv{/Sn3i4fҗf3?~v۟7Ho8K/{}e^_mݗim?'^?/w_Rmтm]8+ۇX<78&=W԰aa觠co.\av',kOƏu㮂i{yLGmqg?2[?u'^o/uoX4;<|9vX^ua^VLo勺7}j!G~긶O!ޣ+'P=UjGMՓT؂l%'zAra>`Սßu i@ͷwP35n^ ='9@%TZ+e!I F@,'Pܖpd:3I$64(Yeg`ma ]:^xDz"8ҭ;[}us"IXH<Ӽoa Gzdjs`no^{Xjv'>wO a/OkbȦaG6e-P_G[oң7oX#R:&]~>/.|NpL|N>yq[[OO?p{ϰ+@i{y/?53-HrWCeAfeaY; w&3+n@ΗLu~gS z7:4AA=!u9h .D^g|V{ ޑcu"fraTbR H3rx5zi> -0_r B.RNhPRplK aBƣr6=' 8X9;n ;BPYH',yX@ߑĶ`{e?cq g;'8R=F¦q]wװcxJO=M]uD%s1M;h%LJoڄ.qMg&!\rk)h|G(88A?B <5@hpFx_pq4 f^aqõfa>6ZTj(%Hl9O%+$6.p) c2% Z IB;>#'8zQiYKCKAV7IEQX&BщnInSx al$Sd8#aeP,Fmؽj-!:[ɫ_җ=VvpxиoA6l4!~[,:Fc>T{ Q&J Cfҳ4%#F”2;WW=s8VeOBZo 1PÉMwV }G^6ƸɅ@j"WJ R\‹lM]dA1qH<\\._a^)QRsfQk EN 3 ?|7ŖcYc lސ2|M+#|Hu5 [IM?1aK(Foc3{=WEFa[ҴXƓ )塚DO 4}6 lnϸ ɳIkSaz*()xel$f dë! ~ PBM 2@c 9?r/0 ȜX*oȊBHZWC]s.ttDXUrv^&1_ G=zRh &p@0,p> /z[&V }QVF>vhfmL%2'tsV!bQ׊܀*"au~aB3qyC+X9 { >Hb0+ky W~&4!|2j uKC.A~ 0Hpjxo8f35q6Ydޛ/F3R^/x<$7QPb2fhlmpkzv=>Gӧݨ 4u(m\JIz2蠡GU&yfh:\#'uH4˸u` CE"TaIzD1)pO ^m$YC:䱈˿D v [xp}HOZ B f:cXwWhțJX., `ar=K._X% H ^kΩˍ$1J7'_%ܾ+-cD$2so^Fm ikdHf-&j3:G\Itdۿ{U=\{'jK1d aDwલqϛJZ{H-y3/6HhNBwyIlk [hAwOs_OBMIؐPWm\Zn-{J܅e79p0b4T=Ugᦦu4 N3!?-L|H~ZJ>??f1oa-:{n+3G 7NsE~"wD#)fTvI]q&gَ7C q4BdCNW? v`$`cĽ|FKL0yFՂ2biI" ~Z`,j)]-k΁6ohLa. G5{~r2bA°7h$yF\ck82̫|e5i6ydlWJ/ƍHtM,>`@'6t옐:g K 7{k9OD}8c Ub/Wߒ΄HS|.m iޣ?V[~0{j;ctHTxFDxL?4!!rN .9EI&~3ɎJ+8^dYsGحxC#wKMrUwah?źz[ ݩqj_:/^ǻ?!, 1r0pH%Tf ւ-8/y3ʡTnUG +V]]O 3[ 1^e3PclHRvǙ*Ḑ䨦_Ǩ4k64#Q I 8D1OMT'vuOc.}/:\宴q@2)^`%G] \T#U /82E<)ٚeeP8i&;LBnk\pC@uj0w%uI,c\ HkK `:4 @sEi,u% wV|蚙V2jt1ZXagUD44YccC Kq:wp}zƑjx>Hb?E^nY:ŏqԤ<7B4W.؍D*"jIA Xɸkt 4hl,Dx8Kɕ(b[X<_IEt@}T4W0ײ1bP .?O̽oWSMuU5VWpRy;*v˲E0Z~;)PeY(&\Lh .?]]Uc ͭNu/4mכV5 X`ʠ[ I'p>(>sRP41;cP)P҂]+\[ǫ4m\[6ۼRPZ;kVƞ̳x)(ë? wPP4ܪ 0v{xmhjxbchicI@+ xq(g9Wm>z~Uᚃ:Zj6`4b7a|OI7@B:eQ5"\3}.wTwoD`H KU[M#NV:2Wjތc#~ǯ5 !YT#*KkVf=ſnu\(#&e^N,1r3D|@YτUs|7x(Qnx?8X¥TXY2+Rҡ28>qni!і6P~xٷ(m@Rm7ѡ{qj=$HՍMvDۻ?ti-$!/K1cS㒫9$?l~D=<_K7H77=]`!/.#5Ίe+՞9FEwDJm̉ M= WX6̙U1s`wbdu5sҼmm^,sS;@ssñߞUHG髃vivu#yWzbcT5+~UpacRďXx~E<4['ҰX%s`į,4\}USTֳ⯐3T\-H7Jۯ%Mis^i˜=CsS&crx~t(%X{fL`Pwęny$;CR3$ mɳw%q1T,(Ly(K*S=Ab_BoT#po~Ÿ7gKyAy=z7O[ />N6I^o>:>MF]xg~Ά=X(3ЅrwG4,귵_ mLh(y=Oᇭ^l~ l'37PTم=6K2ȟORP`0_5fk695&|JI=lA1?;kID> &kL,., FWDXl0_ GW9Ղ퟿Sbc0^T/0Rd9L[* w+DUD%.wdI:f hBn&8h70@N9(%6dDrO8qWIwZgw6LbXi&ILZa#uWh^a-^C$,;" U4: N7I|;φ?8n?K8()4Y>]d]tS D%С9^-<*mAȑqPx /f&IcSHT& ۲_>698ͣ-2aohOMMSn܀s4V gSS9LX/LOΛ:v$$h:"(UN~%rdV5Vf( dl訞` !8uPk{h1; dʅqLyBYٹ 6>K= N5"L1Fr1p(_}cE`#(A~UrIY\A/T[AgE61˪xrd^,*;6L^U; @MP)Fw ! RcG fq‚EД0]A;Tx ΆI'HۨM  AsF_3 rm8.m\F{#FBK+ lƅ烏R5Dt~#=F㕙w V]_u1_>>Lɓ%iUtpxGʲGmt H7Uou&GE0G^]@|jZAُuyqZUEk"0=,buqhJCJ1I.`e)L&_h+K:Ւ%a_3X5z '[ !Nx9eS y->2cqح0>EX<0.'|fc ;Ka*}6cfHŹ߀2gӼgxRV"2aW ;:,!aoz|/0.&U$(YVqe Ch^{3dH|w*@J]3wwVJ{Ged{}ى}yQY[q*#o$ :ͱ7?2-XXscvy4> SJ#w)N +8\#zM oaTwEgw>NHրs4vEoN3 U1KBv\a9tQtC-[VΡIS6,Dkd' V'0|ѽ1^h! B|%.]#Ֆ{dr^E=+֫A1Oh36> ːp+bN+ȡN?CVb`F4I\qÈ .[\pR$ka {=zQlc%F+;H6N2d$^QBT7QDn=|N?ˌB.t)+zQv́7]j-}ܻ{7Pʀ(M.87 ^z 6lzk6EA@SWR5wi*yն[V)ڛ=\zƗm4q0aEȽ>zt>J@\AڪӰ))P*7UM޳kSh*P@t;߂p_ 烓>CٙdzF ( )F6}q0xb ɽl6"^H|Xt29H Ij=nت\);?)8&@峜Ifϧשΰ7F@~̥Ε/'yI@]Ig|J:,Sta __ӝݣ? J7sWcMdud^~݌*+[+`olHun`T]/fK%#=?\gհ7q@M[mtŲ/'^Z dب &{ ~N,πtZ "A&PePeΪ%"$ d(mi<،oIugFv#nqޱ9I.ޭD ֈytv^x3K\+w+΅ЎI崟5,`(b3QM o^>Vmz"ĺeDk&cr=/|2lT^|F|Cy}BhzZOjqUG*A#6JZ_x3l=61yrs"?Az`x_W r+"+fgI~Wڇ|*5Rlu D8~+@,]VKBv_ceIcd>Q\@7Zn+[jqœ{+>;M\Z@6|wP#v^w5n&$Ɂ$H7ً57b%0ΌG+/ު,T)rnCӳ ;#F~FTM/ f S5+ƂhO9j}.xD"gF x.{Sj!DprX }F3!ȋ9n<,8{zY2 fO.#^]$"o?/ Z$46#MlFmϳ(0^ki5JTɫ~;`8|'^P/ >R2ěp="4`iFd&NT ^vW`̠-p=5 J ?J|oTxSmƞf1] /罕ue)OZJvAO#J"(;0Ox ]"7\T.R\dim'+p_|ʹSx`GJ>ܹi8ɒp~gTW$h4bYfk [j|a<dJsU6jfȮtbϕ$Wz+~{B{ ZADmH ,Y3LЫ5Beݧg̐39րjP{{ȓEƳQuދs,Ӭc@zҥ795t۴?P7hF0M;}pttߠ0CǮJ 4UFڱ?nee:>yv,=#]֦̔.&bft9󌓧)d-j4w?=+ӓFE٥Q{-B!;ӋZp``5TZL&lbpsXlL;wohPo..t^1C\U˨[+x-n;Z17NO:]@xi(_pD/Ãz1*P }8DKwW7et?>d4?=asQr3EqLL9+#6FX9_ݭt+3-3>&}2ք1O ?I_okU9 qLn͠ԭ|۲ 1ve`Ūd,b!^p盌*C=ka@Uۄ #( vT.A[+zr9z5`hm"}}bm:ЧL$Mxvp<]y{DPڹɀ^D^oI>8Fw&2h?A1^Y42Ԍ٬w<'%nXQ#Х QaNʍ/emM5 zyGw;MhH04{4,6F]0Dc;(T4gcZ_i 'c8} >@Kdu,E!5dZqI\BvHk@h-ERc^XŢ+K<=M;$"873Kَ =c[dek( (YU:)Q1lDɩ!!VzqI"u^{g9~f97ڋQ瞘궳t0qR} )z^1Z^=MGxn] mL1j:xVg7[E۟\J`^c?-ºȷuְiƚfX QbDX@Ă~Q"͗dWsI!ϗ%qtr׎x8hTr^# :9e %q^tHn) WtcD:F@E# ]jOEqmXHgq En/vFIpBb~C[ɛA$*]{*91nkE& E3P,婢f#V6W&Q |l.ր &&W_MTUSdWN_1yQlʥ|rR.:YmfqI}k3l9ޗgo|W]77 %)ڳO8 ݀K(gWrؔ}&pϢj]+@yYPeZZ/Y ̾ 5vฎ?r:HJsyA'K s霜fV`M+4_62jI)xLbm6#g[OS<4w7P!juio ?Cގ|0œt`3Jmzgg;&캺Kտ%Fw;5.'w-*Tw"O5oyyZ?"Dkׇh=!Z>Dc=1DѓDOn ѓCM on 7ׇi 7i3AUO7ݐ\G#zN$ F6.H4q gfeokrQ5HӑO8{%ԬTmA(J93E)*Wۑ p 7EGM,ܰʀr%֭֭dfzD_wZ`vZEc/90|!`\1)Nj~/_qqEF 5u~/\Uo`ލ3粖dIlVSg_8Rlܕ5ܬ!+Ԫ%~[tnXJ&Īn8UFV ?.9RY~0z2Y'ϘC^BȔ[wھwbr(bk.Ѭ|lrRIډ.ל= 7ҺM:rj{IK~wvv_:z9*Dvec ɡ| yBBwi.]SMmm^mhmq(&PՄ_kJ.a{.Z{T]AsmIU&)yAvk uGц*20w˵ :وgK[)om7+P|LÜDU;YL z:K)yw `YzBhK%L5.ĥN;qSGʪ *c~qM߬Yh>C|w2iӉԜ?cb'gb:Soվko^\mŕI&\eZ9Iuf $UQ~3) \+k^QKuMC//jSm-..M9%b\֋V_-*6]*xɓL{ovɪvyOvvҟӣ[~V?HlK3vD$>r4$T'_YXBH"آ&0"qflѽYa .$lLF0qweڐDž&_?UQnbݚPة \|1:r ahfeJ1drY`]@^gLͿU]vt2{5,o;v-/ZܜE}ޒtq7Fp=M(A4: 13)9v<㨀x** [)P/[E 2R !d?$=L5ݝ(ZC\'!fi쵚`gR`Yrc;$_#4͘ Wd#6P:Q)dW$1KELOioP3[q a1D/y!e.1KgE675"_ڱʔXZݍon_"AJ\4%GH}5!ƲXbg! xd}W C )ڗؾb/^Zm_MAiG]O@#AvJ#cG3hqs2bt179^=+܅RM {>*?Z!\,{:"Fl(Q5aug%9C )x4L܀GxĊugǣ|IR!(q-kP,uE4HE`6> w&J2LjC bM{iaTcetQԐ2Ҥ,堺=ȍ8?>O&.Y+15'~gCb]s\Kq{%#%eJc5*+x,.(`*uak!mmbzΔcB8*6ibB;yTl6HBHf;|,4-%=ڞjqjvw҃ǻгB@bSX4>i5YXVui;s$IvOr'T\.su8B+VwT^47)^%k?/˽{e}{W_#Sf[*6wWGJ:{JI7${G}>*o$ <):m2] +cS'+kʹߏ4H^FX$9蟏^[[Es6? )Ќ~кP;ywplSqp29.};U)Ua(r lĬB|yTJ: I 7 _w X7)¬ԴyL+ӱw/4yIuben|cS.QͲ[CQ0B:2*,~KBEN!~W`t;1m*SEN"QoWF.Ի!&xn̴=W~5Ay)Ocbe4[*N(q˜}p;cƠh;N_BB2=eN{}O&miʹڝі2wˮѬ$VmŊlBZFb)g$ I6fhpǓ'ξ඾㾱'SL@gNs$q~@ NsUR >^qn}of}E[W /K{IK!^'-!Oԋ1Ck-557:ނh7MdP+ weXeӭI|3RU!sf#ֵAVIe+䯿{Ig-%P`qKǙj(F͵p+qw@8BJmgw9͎68N?7cц["LwIѡAҘQt+ Q0ecBƁ3Y] C^ŗH2;^.iX /NE^RK^Aą3x9{uCXd9H^bgXYJe3TߋHʇb0GQw8:",b-EHu! vuĆ~DGq[8cuVZ1R5θ:1DEK^ApU߇Moł6h.:yFLׯ+_{9;5wv{߲5X!M+M;.BqRC5^g=XZ,nD~]|v?_~2W/s[1{FU-?a PG:Aܺ;CJSg,* Xl]#;h]aP1eN-+Nh,Q]`iD"T bIZrnmܮW{)y|h \\vaпy-iMThZFiͰ}ߦO?'#;7+a#8FKpzشoSAu>}y}^=l9}{x  w9HxVEW^:"3P.YòGi{}Fi k>B[j_S` Et䉽.Љ`z:] /\4:Y,,7rKgF] OetIzxSk%vCm:6TEN7v|Cw@qJowa8"~Jýɳ""\DcB*iJP)7"#uf5 H xZ̖FE i(xѱ>&f;b/ ZpfjRĻLUz@SCk?G&Fޜg SOz]PmͥfN;ʫۋ 2zY۠_}Fc ?4vO$+mԊhuD<ï8*a qjU8KQkF&fF9cvl۱ģ:7CnԹ{vGx.n^* -Ø#u2TJb4Y BʭypDfZxeVl+9kߧO#IF b6s6m/6YÀFl [dѩH$ L) Cĩ$'^tݼ&PyBsUfo4 m/oAbQ} WJ (brU~f~?Qדlܥ96G V0X'x:$vsfw|0ҽu&q;VEo1Z<֜z}=U;V5jk7bʛ,@"\UbWo)e Brrp /2sx"uI;Q0Ew 'K K¼{nӟwMx:$M;91K~d<:foUc9 #8ҤlcD&8Ao,cx9=IG7!u6T&A) $PݯEN <b %VLY $vJ`H-UՌ4Rbs f!ьUɀ $uyK{'}tC{[Gzb+Ԥ8w77%Pxvіh''O;D"\?e$<"HTނWWRb0i=@mZnEߖlQ l=J0: ^{ǗػsxUS.Ao`m\D[d yłZKHd}Y>5N=E v+c\8<* 35vWʏ:z@cߥV WU72]?}u"IOt3VPʴ^M''mAqLHuZs\_\`:z,{lGpb^D!ĝh/&sCK݈ܼD=|$K ޸L!wSKx!961()B'=+Y!0levEL U~` +a?*tn5\a )!OGr u 0m"a+1K)DKB <]b7։62{ ƺ7p?;#ӡ %;~-隦N]Ω[*M!eGӳ8ȸNb]vy6 ް t)CM8PURQ|2 .3Hl>Qnbffo/4`~q0=x=A_d(})D0|v` wT(,(:+;HHHB\LJrGsTgS!$ /䞗ڒ|Xl Z~Eń[Ϩܓ/3r?.t*;M VHT h];:NvvhqwMRdAg"=sma-uz_,gazrVװ>=Tܔ巪 YG44nf޺9sSCn>J !c,# ~7\fx,] 1sK03ǐHogf&^J?" -NFħJ.c/ ␨o9 lx(; ?Kz.60KZ |E)n 8˄Rp*DE.dᢊi!V@op9nkWLK9Vf! SLm |`H7 kzpȺH0+"A܈MԤHs}^s{JDф)hS(],qC%bfhf+tIssuFAT/3*⛷wpFRH¼4uĄ$$kqqDp̠bbOi\Xx!pdD "2mzT#jB0ńpEYvPE䨡d-HT:c(%H 8a]Vbmwq 9d!v}.Mfw03v̲v)Ra/wOdP="1P "/H0ЎY&|tS5iwFuN۱ TC8\.3ܺaBW?n4W!fuR~,+;WCIX{-Cgm 3iJ-8~~ąE|MQXZ˓݆@fԂxe#(;i<-!>pJ%V0ov(c}lXI:η2zcG,:8YwRS-O#/gfgemgHΞ*.P5U 0snL&fyg\^]s_}IzϡNWtUӃ 2S,K@'K<bgR S}˹;^tdwB%K`Kqcڳiv1"ϓv}/ko/7g"^ ġ]Nlc8m<2-q)׺s8;6 )o`G,~þ ZjIJ=y9yPe Xt"b:C4E_,F؟e$Q"f1wYŘ֕3lu5~~#Y!W&J;x,"|tՈOg* y槩ttdRK+S{!Zܵ)rE@ bbFy2=N䶖!'Ďm uCXTL`~X  FI񂷗{858Kݣ^ҞKHs;,ޟH&zny0Jh*JW 20"=>R؟$_2E8cE>QG1i 1 ng\ 7UO)Ə *K=\$hLF"ש:"Ϫ2aMcc3Gy-kؘUWKY$WD&T!澋諸t&!y&mӔ eN~TWcNFA6<0(5Qtp lHL;)XԑHᆈ\ę]!/N6։m A諐Fht<sؖhR%B0th.OV~꟞ d Z{bZz,CXSS&\8 vL].b3p\vPi{E>L)3b[LuFe!z3}`мxcT6 70#I:47\-AZ1!#iڴf fBs͗G \!62z8\<`K:pS5EUݳSfy(;y*n1WPҀ$5Waڌ xer?2-@IsJs^L-쭨#;#O5"a׹l`czRG8P >#'dOG; @4ʉq/\3^Ōe( ) IJ,},R'Cx!x͘!m/IӜqmT̵ps{w;=RIc𧊾)#m̴*(2"+ea}Vw;Z2YĀ TJ5  Z ŽGiB5x݆[{"q8y7gԍK%&o ̞ _,-"fJIkj7uωL{on$jߡ#93)iz վV/<\8iWt:$Bt%)tjʢ%[,o6)x"VA;Qg묪6ead[;\W͖tDEs} e T&R@ Ez s`33l)8epqʃ,X_JEgֲc>[1Q<-UTEo:):6\'j~q!-N˪1gj^om)QX[YrP;3*WhQ`]V]Y?jWTw*ܟ*~z6sk;yߧ'^8V(=I!r+iPQt Â/ղ+dh'躞'gb]H?<0 i3a~2P]b7jXj8y,r˅L r % DW٥q6GaJgc&i[ۥڨC~I^/`+B)ĴC(,HX\w˵:n+CzpLR%`viA J,tQ\S~hvp74l( sbs$"3,U&6[ptU m-RԨ!}ͮ^rBh"= ׮ "m|LVU vBgyr娱9\̆YYf{[,}1wW~fHR+ tԭbP킀,ǰCxg|Q-JmU`"nGYeN*МIt]Rd)ԯS+,IM3TUjQhUK^AΧQ%Mt9 XqF.SR͠_€Cj8 G 0> HD|q8g)v`_ԈoDIC;- NQ. ._IU.;xa-VOu į*$V5Odw˒hLrï*F$:(JT U S L9E͑i\eqST0##;+yAM.Bb\w͐j}m٨ !?w}7ᅳ]5z ib=9~H=7FT/i\TȰYd".y4I^;5썑Bl2wu¯]xj򆐾,Lz 3@P^'춯 s5_Jq8CK+V(pG #-a6lWi6a!5qC*DWDUKQ(D Yj!/UBގүܔ?=4*]aQpmN3YqtJ8?Qnt҉EzL9˞B^,C2, Ahsa%V؃`: 걨L`_eT4\눾WHj {β䅡h-0b♡k#_wK ם1(_藔2Tۍxu[j$c .еmtj qL_6bJi*PrWm.(x]U,".WPFAO;Gk-78V!ԌLeL15%<.GnN\%K2pT^} f*ޫ5V!P$.Kx/㲖3HYY gZ)I'>q0V';ƵN$5]Kי\ UYP^= rS$$s8n,=tT'q= わ6#YY:HvBv#-?/N4).B ͂$T}vl VmgK5md TIv9~(ץ>㘠*^"h%JRcK:kTVDVhu7a9J,&,Ftt$!?3&NԐy,Ly3]ŠKE0T >e'&7}=w*xY%FAr"ֿI*?Ϝ)]h p ̴߽nQF%݂aQQ^[zݮV !B<SMYTc,i fp3,`ODJMɳ bX2E&iwUROFqIܠ)_dդF. ,o S]TThDco 6`v%d 73aqK9 ܀ƅL/6b2'1Fn "]%QlYEZBT Ό`9Ȣ󭖊RGD$;&OB:!у@%,,c1dTV "y1U0!o2bÝb%O( wǴ;IDW? ~CNSz)ʨn%^ϓ9=ڳIbl͋kݫ8ښP5REg N XVĢfaFZ %`PPjx=]҂QLFq a0ZGe-:Ӂ90Ek叼%X[Ȭ5n&þU~l.ɝe!`){nk4< esώo3hHp^5.ii/v#Yoc^X5cb&h$cvoWU?_3#!0ě53z=,1.Rݘ~d Ɖnvk`'}[m! j: kc_1p6z{Ojŗ1B!ޕ}y@,!eal}u&J2aA48GiS>I 7Jٴ[D O[WUC'9_ %ͭ֎ozGbܛʠ՞LoPdE`4`]W*쾛[1ͼ߮b3Njj(!mJyPx_3ĮHMZI @Ȅ @|=a5!fK*iQN=l`wTorWCHv̊(,CR^@<1Rn.L5J\5%>khFɏej{[|`GwbPfW$']"QN ;Ð@Fr},LTTxfjF'8}EްS~ױ]_i4@4Y~e/h׬3ix3S23I|D'&ЧfabuZ4KXuմQAN{4jl!$p]r֡+s,s(oq$΋sW j±xlN[dgZ ΐE\i`͗Gme\fG]˚%E)MtrH(J%[6Gp 7U>3Jf!I4<MF]X asJM3w!c,&wkKP7EЯ\2(+n"H\X2lADbUUF}Fk WrEKYL%7\’eO`EJCGLNz@rY7`nGN~In:5a- 8a@KU'3]Vi.u( Lp$d] wJHYM֔U׺َVj9Hc (dYvr٫ÉYlUbej(x8LpoedMd{9.za|+Pl-ߑʯK%|[@.ج-3v4eFy>`Kl~Ru)#5>@]5& ]e@lҖ=:ǡr,2N)FyO(F*E:~%"G, O:nE&*8 Q:FRp%aTu'{8 .a^p;k%t|Rw.fVqISc bqL3,E43&^[N%92"n$h(r#Nm̓9E]kJ]R\{n[I1.Q zooR^e")QKloe8cr a,YJ .&fQ!lYRBt.iǏ}Gf!9z٬m>q.ĩ3_ØmEL8aН)=&YGN1^ bT`DB5xqtp88moIFb  $jhI *ܱ C>3Vd|AG0~TW' sMkӾ5 c)~8Dei^F[I Й[S0)Meq*baQQ!X7_egM[K1@:*YcɩH] @q_?v~졖pj 2G@rb`\;!#ͺ6<ZdT*/B}ctvZ?H@gC֡ڶ\fOLlH^K.ޗjQТԢ3AD%ډz/Ud#r`6٨WQ "#y·t7SZ|2{$ErMK[߂t$^` aE&w^~=D2yL.($Fn Ñ2HkdF,%)% meCֱb̝枸h&P`+K!#5nAQZph0&4!g X(PvZ~ $ûK&B[rܓ]is,ˋfPjto*S0U3[Z*3!6w2e,Wl+jۺ:7WKǮӶJN6l N#*ephIhxQ](S, MPDd1)FXtX k FC6ܺ?IZ9B\IհiQygtY0}jFrb+{Je̓W?26+Qզ#2)f#)/*Aɥ$g#G nvȀ%̷΋k "k\ĽLjbj,^*/1s;R 8 =CsQV  G}P&HDRYz]qpGi7S/a1I6AM lf5qzuV/'`Ĭ +D"@R!G=i. SoE)Q=< WZB@TGkWWn}E^@q FYyIJ|`5&y*"l4{Xwd0z!g2m$a o5Ю.rEIO$$'vhϵF#9sձzu.,q` L4ĿJWQ&p8&@C+\uC_606V_TZ}ED`QRl8lٙRe.aY馰iAcع[Ĝexac" ES~4:#|٣iA\EA4p7QB?l,3͂hn-$ --A DwtX F.I/gu d&i6QvR,[[|5uRH I%O_z<^b*^_>so(9hnnD42\U4Qօ?;[ɻث7GL-l&d0=M.[fW| vTUk6* |Ui@K[iDz2Ղ9+5Q4EZӴkhX1LE]xt=Ѿ% jjRE!1٫ (7ʠ9GaȈĐ$ }Cu6 nm:1D~M/yI[fͣׄݕ/ SIvp:{#ܷr$ϓg?_?V@m/Bmm6DcGiu`mc`IˏkA@lO![BˎOEV| x- K]<7 {' Ōzޒz-v.4&sÆ;|>ͦ5\B4Bn=aq=f6݂ʡ&̾G,1s(8fL7BځVw}(2MC @(V; ]ʪg!wŶd/w!\LW\b)#{ELnYޥ _0=y#ϼV1`).IoMbb-XZ@'8Y7*cVV[2.r@9̢[mERK4Ud O|%>V%`?[U8teoN ?ty ǮTQ(m@EMG66CUΛ=,n-e(iqd17)%Dg@7L,az3 %//6>$_>lOV[džU҈˅c:bRY=D6猄R;1OEn[-Gi|M{Z"^O80fcpPWQߛ:Hȧ~XqNb8'h-DQ#7҃OR61ߤٌ~Gy{|餢6IΡ\qz6S/V xG6/gb*b{ Yբvhr3YZ[WJ [%2lJiG(9H;PKF r7俕lJ\=v?W*~d[Uh/e[X택nG;|m|Z.{Uŋ"CN\pf^4+8K0 i]&\ξ<4 5{Ud$Ie[U$ $Xz8= ]m6Pߝjr-72t[Xf9n(rRq5$F)$0SVe7Li ë+UPmVVL4雉 qnE"!AVP9$eC\:c TAM,[GY]ev J<uFN | <Оgh!/0I[zyje @vS} YuCX! j#>~5I"qP$''֒47KØӁ YocWL#xRQCsߊ_}9ZZV&2奠A,}>P<%ipraGͅ.Q^Uv/d0\Q+){ ǽIUQFڳ̂qdrtx&Ob#'vcZkk7gM؜ tCҝH|WTp5GFX\OU%\'ٚL*Xra[̺jl1zG72cuͬPT}CWW5,FAK Rer/D6Y*u,C ǢȐtž>@33qϪEM30sBȈe)dĺ,b>n5Jڂ%Q\cW0bqO0GA;n~0;/xٖ7ҪW[w24\ Ykz{ɢ]wSL4{%`P)Fq}yt>{w2M:vv/aGL1Yă Wഫl>Źl\}65 6֒-(EorW촧"k?vF'G`d)í8M:J\ FFf' !q=H'݌>jﴅXz湔|MicrAPefV,nN7NT|pہ@a"Zb=*Y`v{γ]MuCLbbWGT5$EQ+Ʌd8JÜCQרժIVd{f;@cb+9 PZbZEcGM-o86 t0+kV`I % Ԫp5|O*`s@wP3܊3*aƍna0\7/#@HV# ?҆yќJ6wvf(qyD;^?xn8YP[*{f 3aH͚Hl !`nm``l~AP,MHހPj '/AHX-Dn9f5[x-]L 2+٭^+¯>Fk05wu#I|gm_G3sf/IL#-av:A u8D <;[`$ #4 q  r$lMT=*1]WEpr )kO[ ]U]z>.=tYx{6CP2GXFfKqe%;]Ur&cI!xsdb&d1wv[߽{y:ߦcLsԆln=% ڻ$9W;x@ C detr_SͦcW 8b`&lEp< XE(aQ  K 8B$5 d.?ada''iҌou^$/˜;.OPyG$e ƾ$M4 \cakM>C!ͦQlnq ;䕤;j!*9VS Qq^&nCEU>e׸f|< Gs1c qb^V TUʒev_ΩBd"S)]vg4 !$D!x"ɷz_:{9󶲘@H@2N=y9Wb[Y6,܆O99aJu4Ѳy_0p|+cuy#3c:#E**>Rs FWJAykJ8/Ϝ_ QХ^6(b4@KFCL:& u~<3kr%8%>:7BYбAen=dH Itm@' C lba( AskwsM?~p\g1 1ٟL폃 1Ӵ+ (75^GI,;{#%`.Fk+l)WT`}@cKv7-fbڧhd7HtǗ 1>Dt(.q`R-3iɲZ Sie Krɚ7m-i$I\R^;O8~%Z&ыp Dp熭gVe&enZJy+B`9|4DgbyH/0Qd=M#4MvlҬ}&[}Iȡ&O 1|s{o|tE4DUfK %llOK's7,\s͑8bC+N{$kPn](a`[] Ӊ+5I'hSz PB̼L50ʝ_,7DΥ7 .Bl&]1خD\`{ճX<6*WsoV|n]ݖY;5»⵶ o/*y h^Eat B"]e ffǰsϧ產S=}m#!!DΡ5TTqtsxנ:=JGq)0wUQT,*[)&UY+$Pٰsr֔6j2}ȟ:g8[W_GƶR.sh>~bJUw8Dtߒhe\,hQ @nh:;{RWq2W'"%G@]u}75$zr$[u;tȕ|]Lxgc☴,-o<2(@yŮJMׅLxhRfJ1\n+Erş|SIOSpڣqߛ\L 'hםg4h!3}cY-( d>VqF?VQQ`q'۬-R%IJ& ^0#)@>-jtUja?do9ױ_Ƙ#yvUNe8{U$T*d R_N)̮vE-.Yщw1b1GV^x<ނnkzj"d,H%fT^KFtagA+xi1oHF\W،55#vF !_RՕţBK/lDȠFp^N+b-E85M~^^eM ց+#B5}՟V})nmUN9oђHD`6v3ĵC2:pEN )rc<7hWjg>^j$,-m#,QU`(;Wʹ â7K~=^'?[hjH1pP !ՠr7'FٵIÿD\ysc^mJzt{KEt( _f2tӉ:Ü9Y7;e*.G%H#FjӷtA(,<~7ʹz+gAGOhMa1Yes6̠f=6N9k:«6{lBpR 7jRȠ4I{wx~!:x ][ rYc0ҕ0ݿTeg=L37Kb>`;1/[9/}KB=CLQj|G"eIvvMQE,Akkw;;G?A&vMP!eۦް,b9`[,{f;vig.`gڶTNq[օȊ;m]sMJUG 5C*n0^E#XWT# 2_Cd$+<» `t.>'a_=ZE$N"o4è 1b.[9F-mM-|R-"h=҇bJ7Ͻ9.GgLPb|쵣#"ABО}:ky{_}PiKӂr7U|:#6xO&8ٍE;AV0Aq0ˋ|&JUY %fRnm5w)5a[(GJAa-d e%tG!|#} GH:HqS6ao] Y'=Rt$RYC~Pn'̪h/SMǞ3(w{篗dWCNXx\Y7 v=a FrA M) :!Dž]gv5i$'V .%,028` &qLsSF>xq++>%8oZ≒< Yy.YR:/f>#6/QM`>?DП:mu0vjQI"ʒP8p1!T zSg12b/TᠵP7r2/c$aQ2|3 i6*ma"TjPYm4:U0pAǭ!.tϒ9j m3+%Jm4Qm'O PnTjУ70n0!m ^L# +LU_h7%iW4&L+Iz$^V?x)SLW3o^9tH&I?$z:agmI`tːm~&U`ًSWW죊hY)$L'MVVxT  oJC){mV8nXQo#o`Uƺ23]Q9̘WU9R$ynU' +[;DV@ٽE}Pސ "+U|ElX4|s5D̄,hDY7߰2}. 5sҌ6]iܕo4Fj䍨GtY%(S9eˬ,$`V*(V9RGJJgǹ4JϥQ<Ɨz鹬ea<,/;Gx.\PLđw`3as_!d_wld4 l ujJЛu)?N\F1Oʎ_,5~QERvTzʍM4'%Af!%&GǥfnHsN)M+!J YC(>(Ae{ynEjSRk?PוOL;?AY*~5ςwXDsZj#1[^1څIЪX7ͽw #O{>u B 'm6\m|!Ղ9P *9%Yoq 0}щ8l$ɻO`BdIorT9qD hitivIA'g6eFذ cƁXO^T 2YتUηIıY4P2YM! pLRL&o)j?)9@KF=>0'ҲcA e+3B:x⇵1젱G3U\ݪh?{Ǭ*"|A=%9[vw5R&1jT>dB/ ?MR\4CA ba:{>@8CphKȣ|H xAx7xÝЛ z`3H׊L_ S )y@7( B8 t2ƪXm_kߠ7u d+(9#sVѸf;jIP Ǩ!y+wi释F%مDݜ$: i0p[Hx_~,X5/A'W)j=R 39WxXtB,@^ Gi!~/8A6q i$5Rcc҆QA-(Hx6lUXLū-Л1sk. 0znsl߽ 1'0ѓ7+v ]ՙ4N Oc^q7ekb0KY/v7iNyO7L۲AkA֋+Qz>\ QP^&j'*l& 61j:d&`u:wYk5g?tPV{h$'ZL zaO?}qMBx_ϖKWK>>a5\IY}Zx 2d"tcz!뀽Ee _ X<2M*Z!OtĦ :",}5ZPOL qpX =|BPP{kcI&*>(hdA@I$MM&sօ G7 ; 97XLf ԒY [x#wp_ioj/o ou5YV g9VזLT(ze"P]ZDQGF yDƊ ED| >w^c`jʄ+aF*׻:`: vՕ$ZY@] 2hێ?D$#bK Ux?PAxW#-eX/AԺ<˲Q[``7Ò+ꂍ\c{:}Ty3x|FI<7F3'aAl?Z_#tcג:\CT,tK;dk=o;l7/bĐEB0L~ Qda5Pl8h<_ONX8%;Gl)w5oxL6Ъ t{VogԖ}e@@a\\UDlbS4 `LGh!S'`IWH+hF]?_G~NidʽiwxIyr쵊w#s!W]Mݝ՗2R'XŋU$x( 3B$;Cu-LSM~PnòΛCLi>M:spf.ε\'q\@gu(zaf7wyX-yj]b 0rߊT P%gbD=*[1Ї}x+^<Т73F@&oB_bMWZ籕? +f쑖!\Wq ?䔾B!ii&KG0+!i}Ę΋xaŌP cu>Wvi[A{_uB[J_rbeTq/Fݓ'ng@Ն]|P"GQB'F6PR¢Ҷ}etd5媣5k}wRp>mx9kXI%q[E& (aSi lI&Fq쭕 ILdCJ0PE|b X`BypM+yL0iY^P^M_Ĺam%WYV.%AOIwjL# _DQ5r0N%qyg١"Z{ $knmbſfC+KL}ù\ΝԌ9G8CũjvQE2Jj%a sjSLWL4&"h YЦ-Q|$_k8_Tbvg"c:Ry~s\Y7-ݙ@%^v赝iUfe_aG_nlpȧy`#gqK6|=SK6KV%_֌j\nDѾ{KXZۿ-T cA{J3f\DK^ hH3߶ExHh7\K7VUۨD4OȩsoMתI@CUzzt.I<[B+RBjyfzO-yE \=<R3:nKZ|aߧtJNx0#Sͦj v5nC;ҟS8UR-HG=IIِ*ӭh!"Bמ)#'%DUN Vdm(w4۔~6ºm'L;o=Kue3,%= עVYvvy~NƽQ@HQn7y@̱s $^RWd/5:ckzV EurlIp~SzhY_]Ycj4yxnmmn|n(*v]ECcYZ3q吷D; l窛Is?ɹoYW}@bE:ǔtf2>ƲsQh-?md'T񁖝gƿ*\,"4jy #ѹ9r/i`L29E[ٛ:hJ? Pzղ.eC?4CI-:~zU= = Pi˪„f ,`!&IVlWKಫ){ !*bU|#Q/D!GR"8A(!]A@b.`ѹ Mΰể&0o (1ZVXMwu(hi<EdD<Ƒ˱%g 5<,5d#ТIG, F\OF>Y0ce ή@g<[sX\%d=o'dNQ#;zibOS{8K4 KxxQwӸ"yꌀ罎#2Fy"ޝfͣ^ֽbCŮC%V,Au*ZsZw{ @MAwed8cK{ - %o%ku薬C+SAJrn,מE+5g:2Z`-12,e%0*D=IIF(DF٢O 2eZ,dnܞV*{äMv$Bh:Qc2NwJB|M$/*av#0W· E[Y`[1 ?\|V#9P;Q7|h4DG<+P8J ͧT)w)Oz}yG 5'hN-F9Ňstfpd3W{X3*zV5=Oxّ%ո>vE;D2," aPDURp"QE ~7E%eTO';O~+]גD\R}dTLbmx9oL0u~ckvkQ c'郯8a 4<ϤWЬ:%kB` I#&c' MnRON@?*!\!r&آd<;X<6CnprO$Kr ~D>JMɴ4*2+Ui0O׷@j@*j7UR1K‰AU^J{\1-Qȍ yA. ϸJ5Q+!@,A`a+Hæ~bc@0w]4| Rc [@W:c)X-0g, ]$YiLhtB _N_ 2OܒݪTRN>kVYp%&jIhtpyځUAvVGjpԺ,^5P[Zl©2^xԼ1o۫ _:fW>w|h Fr^$liގer&m0LE%¹{TqyDP piPV|ɪX?y?ykMf6n.= cj\*cp6!s=bUmuU:{S;eUZ$dz@:4esm9?0Q5(7O&,)a!gw 5U2|^%ʥ+u*N2)|)d鯄{&vs#eE m65oCzCZJs:rn~Eٸ'a+i6+m쇊BznI8 ^ZY+|.Rֹnd'f-b@ׅpszHZZҨ5Qf4;ZײijL"-xʊ-y9;*"iSf<)RmyW@OVËUf'Lj_F΀E_+T'`9q?ÐWUmU 2|`GOcVQGlr!w>.} 2 K˫7^%}hA]%e[ûEwM|TXtM߁#~P@UNVh*[/0Я:;_č(`:@lMv>|9+DS3~و!!vIqﵹ͹ˮz#?J.ܣU< 7F#WzabW}X"`D7<|#sCu,[ ruc]_UˆިP($4Ԩpl#2i 2^. -ΩOFD).Wl7$ e扽 ;mo?h:Ae"!kd}__뇗Hy_J_H/H-P1+d:"ԷI. 6Ǡ]  @G>v;PL͠RNJSi]j'ma5f,ջ]E lnk@kї|df .TPDGeK zq}e@5 Ja6s"iM!r͍߁6@)JVkHQK꟟"h1uU#f47Xam#ZtnnohMAvQ(T@]H*aٶ1|pQW?6s!Fu:O H gÓ~YXmQo8$Lc",X_K0*cJ@K.IHɲiD0F9Pp4D|L/VSMVu @o%ۓhUg%vڃcں&)$  r F⊶) Wv \P8ikE%8t=]SzO_BCMaPN<\Oo>Y~#ȡOS8 (}g>cZ\~=z( DlczPca^Əvᨘ'(q]YoΡa&/8-ntF/13r;'*`H&y(_-I=Ƴ2R3o+P~b\Z1 \jO씙*@y2'ʖx/?;6]|pG?hNNW_у._#Lg ~u!|{@ȼ L*96HR/`Bŭfh'Kd94j1NZ;{oCc͟prհm:u84 .$gl۳ˬiQaFJR-pчF|ħVRkdP 0%&VE[/XL<:nU~rZsr 3{n 8 L\lβ㡺̵[Bi!ph#rgzv&ayCC ˃@BH,Z:5psA7TMYW?OO6}-zs2O㶮Rl"Ec" ^3+7桯'4LTvJih {[ZMO|PJJDfei zV.~+ula^)IS料6Cso{F173ۃmX2H:L g8M.ҿڟPt!L$Am1ǰ}-Y_uјꪝ&/2K|tFzE8ڪb#Xw]vf Mf~ݨb αF'ttmA~nԀ=}.^\-K [^Iܲa0p=RX%>X :⯙珥 tPf*-~UǥdE=鄡ɊpnH!;i`E%ĦCt64U{| 'q/rBV"T2J@Rg!$2q`J{T*vK'jq/8_RH 9a%5MW6'7]ɆI}VvN f 1PNyuMx NؔY['};-ij2=c,>:v)yx(5c\LEO^'!㫟56PN?\6 0^1_QPM(ݔVW X8{B}uR|m%Irr+U\5^iZ1yTT?Qx_ˑ~j:i7QxXy"?|wЬ_ܕ<> kq,]dñ1-eM~Һt=}`/ˉ]A3Fu1HpsN_C 5tUu8Xad!P*zgM8p~s`$5\5pU5jIʏOȪNI gs0\|y[w9 ^pM$٧oϓ)msA" 2gՏu J4sv,V@wWɖo)l锜s[`4M$`ԏSҎCRב?U\CAnz<=dUzdMgTpA3b#nI~rIٚ?K9Put8c4$ᨕ1` X-pc~tCg@Bl%Lo;'y0szl̶0l)L!G$#JO-,ͷGʉwO.;驀 ۵ ɠ|ʫ/{슚Bo*hP~)K?65C(|E9Z'ݪNvEN^_4S9%);Cw+6 Jp-C&]?_[b3ǵ}`mėt*@0m 7e ,bع->_hLDd?=J(Hё mtiSdIh $ &M&)Kp;뺃3ڣzq&8i&ЅH*z3Ay >UVo 4ih1uVZjm_ ]p@@\0g6YU~6\̈\:"1|kY޳aP5E֬H6%ChDR٤+Np-q#Iބ/B{fމгǧUP!Otk\:GGĭi'ُW\%HҮǬA<.sn7hzs:*;p`g ,h 8e M[|i(E)-ghѝvB0sʥa)S6+(ڻgH(CefKs`?; ?;<Z"V \0T [5+\oߕajY{pB,ai@`e!{ L%2蓮6Hf T09r ':>0g)Md{Dd_1pPQ-`#X/Z$D_'MJ) ҽ ¦ !]~{w:z}~WUDvD#$4PFMui[ kAvpLHǧ预+K 3߷ eqp8׏UQf4g"y}<%D_>[N 1c!wNsEe/:g*J@QRpLC`I :iLd(Ѷ!k| huTh*'X3fAo $ L>w" F@^V"*tufw=uLZy@ -@9?YKO'&$ZSd7I O%CIt.I2 !ּF)/8q源+ k[9Nޙ[(colbR%j}%(:vm̧e{ꊙ} ƙHbTyĢ&(BVN e5$`\xKoc3xxwr)?./ɝa| T G9opT.`\:3~zkĩu%F䑹j)EGQRd)G6`RwA:\3gHE=D"'lHToh2h ]L// Rf:”iȘ /[xKZ)O--GF, [-E7|!ޥkv!NҴvW%3x.okPn].jYnڿE5/txAc(8l.~T] Tfr҂ok`P>iV佮Qhʑ>鮔 ̽/WH.=LW Eh˗ V픰u,&_[fTD( ?zRۂ{3ORL/k*An+چ>4F6˅tGeȜGE`K#d N)0Dq^&&ON 2 No O*:;b>j)sigM͜oYh2Yw3Vй+9h14 vtYkP`PߩD1]a4T g8Pˡ-;mөcY#e|kA=d׼ P:vEHbF=Ʈ 3S~iԷrǵ(ǿ$?mjxp8ֶ{0am@3T۴nVơz1+ GzfP{^{׭mbF+Èn 4C xo)dU,.|SRog/Yv ]y'EmX\9pEIgwsNӓ0mub{͜#`29nNU8խl[r{@9Y諰7!w̓t,脺qMr/GyA 9g S9|6^5| / c̣34#T+x^,]fyEAha=j9<.3XDnɏzćVFAp?4 ʭ9tUmqCء8~øHG¡I\.Ӣxc9sq+&YŁ!"N w#y!a^sZt yaTJX :-/ _]z+O J=nӎy3;f["Q\+v CD!Yqޏh쑑3_qsh$G #S8uj=/擛6Nmooh(ܖc} & $ `)'?ݑܹuPE99AJCxiνH6U0Dđxqޠu.U6MiVC\( C_&'Z晕:B(ZVÓ;?I,\ a YbBJ"Xā2dme0D[sy#I*\g&s`E)vU24LE b޳6m$YXU]rD'eeOk)9]%VNs ! mkw߯_MȮڍ{zfzzgr^6.3:MI܌{B伸>:d3},orDRMGXYʕyI|^- \%`1d+ɱ*Yb$ռX5ĶX.搑UPӖ>ƞ+Fg&.4¡>VINx7ذH. `9\:"܄Sĸ2.g9oۭn.{~`g&ͼF ^,O׿v7~ܑ }n۰Z#*qeZ ZhY -cLAÊNia/nB V!*07ub=p eL6&B6m0#hןWa0fdvM=I6(l,J%Xȃf8Q BGcfW\p䉮%-8A6v~dfo ˰*cO8-6QXegY]YUMYz:7^͠^Ď(,҉x EziE"1,Rސ;ZFb(ӟKJ|Aꈪ=Y/(?nHl4G .Z&nWeE5"5k, ) 卞=LϳvIk*#} b޶Y0W= ?9{dM1ơ Yv1%o>ʚHaGtwJHЯSݩ8$HEE"mU,ؔPl&"Ku_D%P`nb>mG;\ˈSBmc.˴-wOij(;e'}X} GDWXOś]OD̸}??5W,0] __6Am)U028Z-2/?%G HiF88{::Q[uC~`9+D1?2!%kЧE[o-o}L^|[R4ǻzG.V"84j历 ݛ|~Mеg;#\Lh'O󿜝 ]̗l5i0~vA73RF}߫5 uٱ{7 !AZEOXQؽfFsP 8DkL}pR){WQf|;>z}"3kjR@b;պn=pKo簞oeK3!5+"O8d ah=R}L:IC&= ;JW]F$h=w 1y[W k#~dnƖIfԏp|.jUٕiz[yTk')/i ׫}ӳ/,/SsQw |BSl{hY\j5cMSZ$mnᵞoi>Fջuoi)^\LDx$޵v#@8U@eWɴ^˃8gڻ*I0E S7na*e'k~(<۶ b, bopg"oxy҈=|X$֥c" 7c`ruӚ-u{tG:8YY1 >8HQjP;P0LAjee:a~Z&UX-90'Ȓ8;,9 ә^_d=AUA:z 6O$_}ls[0 2AX#$> x3ކ^뚡0'& &tڿ+4K砷P\Yne:BpL2g-s hJ[xl_&ݽ,ObNy s Cq)I)RJ8wEw &⮨p:j%aGX$0= [wjݼYRgѭ1N@^(3J¸4IggK| 늬Uv[f1/w N ԭ:Wk !r?!m^d]BU[9jܸ': _݈q= nl~VwnxZ UWr'_^4Gs2(q>J(t1' mqZIU F;9TeqH[WB܈2Pu˳~~t?axzO?ʩa5(V!*TٕYVgߟ<ᗵ*5y*xXKI|\!t[,hDh1|^ ObVӹԁRMK%#-:ᥖ R0o)"ذ\&biV}?T;Jo`/~ϟ^~BOvѿn^~ TF! Xp7yoO'zjAx ' Y&/x9D!F.8B^iڭb#7[1캁 ɵVO{o Uׁ2[ SP PQe +;&h<%D D8+Y8R0*ꋍ?l?E x|nt)Yn-aG.kD^~rQIySexi:2SMxׂbYy}1S~JZ^Uyݵe"`}BoF |ق) F:%xP*! 2|vt :2<ڡ u(IY ٫"g D,iP;<]mw܀'ҠTOY~ |Skԇ)7]"]ے]i/oH `MɤrX=6) - uϑxˋKz3KxAOR_o/''>;YOARi͟(^fj< H/Ity LޤŒoίB"yx &0O~8|y xzunO3=Щ|yAJN/!# {YxQvDg.D֛:/S\nɿTP{T|,r4A| ߓB$2xEl-1+O$l>4*+!0=N`;ea6 i%fN^xG(,eY_Rx? 6l uiWA lxzu;TopٰW\ܛ`fi]׆ %'xOHq++qn{|M.-rEŶy_[^kN  ⹔]:D^(0a:%DBtyx}ϗg 8bic3xmt+kKf1yy[- M8.>Zq'Jϝ&Ef\'mVkm6TIT ޘU9gjUw7 j#Aԑ@4=4Yk3z%͡S#r*4iY)!P\ -ѫ pe, apzG-mt.ϡRpi2]VYi* 5mi4~x} gK";!rcXjlltx)Ӈ3(᥈En_s.kœBI'ܿ}縍v+#ʲ{U$hySf$g|i3'Ya[͑^mx340031Q,(*-)+.HMfh'ϢH*PBx;, ,FzF87+Jtc?hx;zu$F)aF!#.πx7ψx0W[#< : F3}t fx]?n@noMeն;8d\i^F;100644 iprlib.h (>j5g^Bz'cx}TmL[U9m>&][ FdD k\cY6W~8q3:;d㔐8:h M1,sGoUvsy>}wn;S>h=jvYq݂md61` j萬#6yE+&-kG[ey+=s; ٱ9.[.l-pT +j6IXX5U}IDɡ\؝KS+V!ׄ :B]zP{킖 {d l A|](e]vTщ, RS4fHQ\4߶ˉn(YRI URİU#1EbO`hGl8^"PFԥQ ~$Y}1|N«%"5>`KyKs-"! :~ *\,"4b+\ɮ.P~ve=aݰqjivѧp1k1a?17lo ,tń9(1~԰Y]˕*JOwclmxZweܸAyͺC^EMP x], CV0 |Ax%/恇Gш%^GcJSN|c>l8&^ 8U&{Gl.$:xć$׳7UZ޻;WeR_l;g;t8kݏUB! ]o/>[aGT9F>R%y2)/_ VXӬdҼeߴxYMDx|@o[ҮY2HlC":T B䘎1ˣR\c+08Ea5ˌ+Uzlpρ'eN1HúWL9EnwȑR=9=>ܯȆpc/au8vp!ꇒH?$&VLW 3~m=.?}C D < B㔲cs7=.5NV8IX0Ux{RXC c S  w3H+'(&(d(hiS|MYڻXw3l x99xu_HSQ9{֝߮ӔH铕/ܹ2)z(fv= np!(z12襰$H((3v{<~9هH_Ha)6bKXuM<3$yiD.^1c"?"/RX*u5Kpҗ.?Zkſ@8 x=e{<99")Cp](w+ߌ2;^(Zw> ۅ}%6\$9wi.iɻ=m f߫,qZܸ߀inbm1}Pk$e㠪k'X:GQ"ts{O?|"%t$A W=vH} ԍ=ߠh2:qh4`LTgQܸ nR,]g iz ɎtvX'orpR'q`W k<ݭX"`1n&@yRSphLUs W< w3,*Q.Q]sWL&bBmMI9,Ձ +`t4be 8#}xݙ')='MӮsp5D Ӛ)*uEU$(kDX@TadT]HE7INL|+%qUx' LfJ᝜*y]BdC7T+p%O6l-JNѵ8;3sbo&/x'39Əm7}]Ir-xe?n@noMeն;755/8J&-6[74k2I`100644 iprlib.h>Hv(⒙[妚тBg+kx[>t 'Y3S&ǜ~SQhIF鍦v 96=9qz^c ab,u=t3{`5y ϭe:8J35&gԜtQ}z:F2Won&-LLOI,IԵ+J-.I͛O_3|soܲ0I ݚ֓nGoV̗bF6qs92J5L4f>7 Dx &&k*h+gkhxvE9y7 O՗JI,I*άJO) jn^FwyeɂSE'4x::;{Fn>ҒR2Td3]`|1 B5G{xzuA+Ko~Ur,ߞrL% 4x; V)w _6:2,/^b reserved6AY~LWxzuHiU#OoWorLڇ51x. Ւ ۚ return -EINVAL;mAxzu.y;-aQkBom>˸ev x{=$ {'7i^)g^k&bݼO.d~r!Ӣt$Fx В ֚Tm x . q'ǁpB6~x[yc :LfT-]AtcN;{Tx   ;su x. ۚ return -EINVAL;m.@xzuk}g]ìM9Yi;i jx;gd {'7i^)g^k&bݼyE%E2n_S:ȨwuxRH.v mjo`ّ/>=}ܙ>~@vA3h3fݓB"{xc 7)D2m^j fel U xD 8JzXcR100644 iprlib.h q'ǁpBߩSxzuHiU#OoWorLڇ5[x:H.v mjo`ّ/>=}ܙ>%(+Ix . q'ǁpB6.xB:GjgFL&~}<E100644 version.mk7ic05"MBm7fN4?ɩx340031Q,(*-)+.HMf0}j-V7-9s')ux;y`#=#~sd^&xzuHcW?ͼx}rLW2x0mOoob\b}ﳘ"Yx! x?@! ûP  x{RlK /7n1?757R#4--HGar=₢̼4g@P|XKKo|gktu̪;w213or7-(xzuwlֲ{:.GPw=bd9*xGВ Ñ iaj019# if (has_hdr) { !xS{)xB:R {_|ĥ100644 version.mk b܎%ta&]@Sx340031Q,(*-)+.HMf%eqڪqpr9x;4 ,FzÃ87 rdx;zu-#d{FYĢ C)yRx]3@Km:uptԽl͓8x ?/100644 iprlib.hA^/bQ;-,KS!BE%1xR#-L 3IMEnz@%tB&'1)rf((dǗ[sqMIiB[g1i+%dT%W((*(DAhN?Qx& '1)MWĤ$&sIS3OP1a 3`;#szLخ: elI,[ax} ɐ Ǎ/a/j-Vnj0 ڷXɡ/?è#oif (has_hdr) { !!je{89x[} :LfTO1yf!277flN.*mq:+#ƻLxB:oy2&X[.f100644 version.mkj$j*3ϖ+ "x340031Q,(*-)+.HMf85=P}WF*D!yx;T ,FzÃ87ϓqddxzu$F)aFG#.πx7ψx0W[Cs@c!\1V#8$57)HLGrs%c#׬WVxzu#d{FY."c'x:6 ; ioOxzm}!N'$B\x{H#-L͓[&113loB+Ox} 62nʑɿ@BcAb &LXOCC3⒢̪̂Tk..R ̔Բk@bQQbe|f5dO\'Giu~E+O>4 !,'Fڀ !&OQ7YZZTbƷcrGE,ʨ&F8Jhɇ4'NJuzDN9%5-3/urAdؔ;e 5J354@<];`@iN^$XT 4f8nI?PbVernfJTڛoN^2٭!wfcnJjY|Qr,dlm44a(r@kD6oi:YkrUg@P|sSkP|gk۽̌N`4,530@m΂؛{g)r9`2m~tydғ Nfr[!?TL~[eSdxt3^ֱF"ssXD /17IlfQ('3?Q.#$>4Ws}o沂YĦ_eUR@YxPDrbY$GO7?$1)$dY,B7DO?6i ,.LtpuFVI@CbYWF(0"b5Ed,יߏaQkvX?jD\{x;9LφF#K}3}c#c+c#+Ԣ3w02nb9Ē_փ43 K2tҒ`x_Lj;MwnU`D1irBqK'X5wT7.ɨdc`əWWje'ă \B<#3'UA(YWWkiE)Ey%̛֓|{9Ug&|gdu 7m k6o~i]ag{u"c5_`.ht`z ƊlOLRx;QLR Lc+c +ULSP,,N+/.OI-LN̼Tk>/0M(˃,,i&FML׷_1a8 u'թx340031Q,(*-)+.HMfz}eXD&SY& @x:qD a87_fMkx[ JA9ޖe'Ll}:v/3 ΋|E 100644 version.mk0&B3@jw'x;?FQS#K} }CCs+c3+c扌Odd:i8ɘ+&Lۻx340031Q,(*-)+.HMfذjˍU w|1ڬ<x:}0FAyJxzu$F)eFF~eIE F:@l`91 4x;zu$F)eF&Cv% &: Fs1 w }xzuKGNpJ0K*70D!(9?/-3]/A:f>1Fgs[瞜,^]<֙\#:Y֪[GWY+w{Ѧ5,83?O/7ቊw'%Wxm(&=C!x58 December 2005,O79" ![+\<e_xTkhTg囹}ͽ* 1+Bd4G5MTE!t JҪh|P(+%?PFR4?*޵QLv}gΙǙ97+g֔Ne[([DWڠA$7mCaB{+sqᴋ)ӣɦ]bd2vCѱP_d*J#MVlA^*W7M}Y@5+/*}|eSs~[D.Z/VNB! jXDxs=~D̤ ]'G0 RvFEKDQ`p"5HcvlJd Sr %z%ӊ/E`TNڍD3ƧKh"2$]eʚY?t]^XwUSml bB(y v5puS~N\ 6U !I*Hx j}ǤJ%DaVS6~[#z]b櫍P1ŗx7BkN5)0Md&X9y<@sH&vnHO#LDַЩgǵ?[Y΃-0'nznI^hм9ٸ-. ȢO Blt30O C_Xo486wlR.b[-B'9Ot x@<#='1 5lBn r);bvck\sQOs00@>ͩX$}@۸Gh1ּ467<~ *)MZFG7KmwfH*x340031Q,(*-)+.HMfR|oW|76NE%x:qj0FӍ~Sy lK2 x;zu$F)eF&Cv^y : Fs Wxzu;HM1>k>,+7Yϝy'=_mh``fbYPp=yi w}SKy_Pwu-j.K-*fN=~xIOt+L7gMCx{W]-HOocenn*ݺut;%}UkDx;s?FQc#K}}K+#S+C扌'22LWbT~Eq1OgTQez;0n_$tAQkzgf^ZBnbNN~g@Pk|`kPd|g5gnjnqjH6u0i@`7g|&ކL\e%E LIx;z= SE , L , L 7bl0+Z_K!%5-3/U 19U!-H$#U!#51%H!1/EH$(WE(59(XAKK3 (5>05(2>3h+'?kv7_f^ZMu' eBԵ[PY, @ VltrR4'[rx;zu.doF.v0W[v.ɩIE F:@l`916 9x;zu.g@PgDkd;FYNļĢJi1 %8x;zu$F)aFFzz|E9 : FƱ yxzuHokޜmKO^|G|k|s㣟LsKC3̂t {Yiq۳Ubz>';1HH:m_[gELj^3^_:"Su3;4qS\ŷ]dbr,83?O/7!ɿpBu/Oj] gX\#jx5[Hqg}˖\"v!&JM@G9c&*B+)*T*|if񶐕t@›NDQދ}xx|c^LFCRcMH#/,џ80.VHX#V«,>, ~9)٤CZt,/a]֒a E1s"YL*L9nרmj8Z߻@#ed3ǵ ,)U"meܼ& qVK*,Po1Pq'l{Viei5sJ*y&RlT9QBXhBhݲOy_P+(;lgEz8G%lʘ&ZGH-Xw 6B2bX$l{D<]MųfAWN-mCLM/NWT+4Wćfp»D'{ KL%D>Q>1+9 E0G+m#Ѿ+V+k~ݍgx[V Z6/leaL|ۖIIis|N9yٓډ8Ļ%fpq96w^] 7Px;c 5F FF VF@x;zy SE , ,̀L+S+CͫDr2J+*KRRR6˳\lŹyx9sSd.rIgkrZ((Yo6YjlߧEtre6n*mެ7q5rγOv=yiM6+̺YHHx}y SEM ,  h*7ҌNJL^-daY@l\zx x340031Q,(*-)+.HMfL3|fi7]ت¹z3 x:qBp?YJxzu$F)FF~eIE : Fp Uxzuqz|G?f9PYa7_O*"UV~g5wɔwSNj140031Q,(JKL`xh EQ`7J1k<4ى1JWd`M708H:rT 08'3I/~M!|)_}12znT5 W6wˉվddlb G& L @ 5L^Y6>)V#eEřyz < ZOζD7wlbx;"Vl8f=F1.Mhx340031Q,(J)-`8ԛ]˦^5o!\IF"] nOs ٔ=tJ̼bY؆Ty%)% _cW^U^Pb] p<x;̷oe,uXę7zlXxoC̏'e|xUOHay3&m;J!_`KN&$*ݼ樂t<Q}WKԥ:t@Z .gm :=WRGo⳯Hu8R\T[O I=Hn@*3*},+0<Y3Q7"&!#-xsF˷1=+KL$[ܮ"@MN؃삨ƧQa^P޻⩎gb#rwD-s†Cyu`ChG~z8M6;ªpYa:dDffM-Qr&|٣ddCx$ͯp370s7C$-+0s Yx{# 5 FFf@yDϹMg⚮=Y(&W􇢌rU}y1jLf`.LL~Qtz\ #9ቒ PYwz#|Fy۹3Jl7f0}:9KS0cji¤3}9S6S&u{Y0-d9=2v+LdFCRx340031Q,(*-)+.HMfVtWSjH,.OG2x:qB XMZ.xzu$F)FFFf: F) xzuƒz:R'Uy 0J|dh``fbYP sjʇb& 3wɫ &{SV>q$Q ʿ/b8b;9qw?s/n ]eT#td&Mn}p-_`d'HYy+S#.[1vԢmvV(N-)K/\!PXZTR'1SnsE=(*| jx!M0󠯗qQ, toA ͙L^H]X& Cٗ&JBOD< P{ᗣa\<Jn6 IoA%*!>&YaiUnxYͯ % &p5add_hot_spare, "7"38#"1 *(iGE=}/(2ɥX $#x=kQnb$QHFjR0$N5V[]D8&H4j)0Ѓq (EѤu)ƕ/\1pysgmq1)I!+(%Fɑay^v z(Ie;vuWo"蠯+D eZ^3 -FaJ5Z]oqFֶXLGB\JU[N&|} AɪzNLkEx{yq SE  ,Dd*F?A]&_xrIޛf-<sxzuƒnu,$#<440031Q,(JKLKf^1&%6ze[=rY e W{(ή߬a qb:BYJȨ߂1ϸbCm&is._4~nw~ږu+zM$ \DKQk=~DctT:Q߈ڭQ6&SƩ;bjl\z%֨!ڮxB]B͢i!*$[IʀD˓@(8CpuoǫڎrSTGw ժQꑴ|-ZL:]p^J!Zײ*:K{:txDv7 AȳRɄ{bO:޻Li+ Hwd~%dZ7JI(~e{IJo96gnP;vw*7Ǥ2&荲Ky2;Nw~p0ՁM*hIusILs!(RU;xV!}FàZ$nK=J@)si'zEB}`g=kۂHح-X X ip_ado(MRV5u%1iQ)bNK PJeCoDa4KfpDv'; 49?_ )A F_w"Q"5ҤpD4.pCMXݑVxe0­d;R]z {H@JCFe17K i7\ЇXԏIHGQ4C`(ٴͦh >̡+S\@-֣B:"zcwpB.Swd >TyͧS8h=+ /ҡxѶytb c P].4RsCX紺vi髟 Mf͑VA TA(*(_K&41wKK#X'#YAWhVJ1-]'{Y7,L`o; <ّPoOԊ\P%@P%viCfg44 E[WF0;K';{! e0 ,r"p۞`<3E챇;F?҄l)d,d E0?xj! X> vEB`rv+`X^Ϣ~_P=+Ɵmˣod~pD:b>w,=n,eiS}xÎ=l$W2AapCߘ/,[ n?;K=vt ˇwC-LΝl,Y/ٛ 2Oag" '9 Ya0 =$ lP'[>fȱ៰Hو6T'Q=ǘKl3[Z*rS9*n>k56b33~aVfWNs׼0"„ޅFROr;ۦh LNBpM ?MZ&Qx^$}y 7;kbC9i;f{T=<-6sZ]|TpI%`)cDH^Y"^O% W-H"~}żg[Z@(+(} M/3$y4U\!`F/tcS9|R ™iJ\qbaƔ];jqCK&NF#f.N͔plK=#tnf23x:|/OgT# $/BIь~xp?<߹-? r~ozgp78&#_qoO.@RTV 7s(qJ:\SZV,Vu&Wb+YB[kpGW؏_WAb & y8=#rޟ@$j{'J^ ÄϗIM'(Xѭ8)ENZG/.h9&2J6BWiyN6h+ߜ jQS(sQUY'4ٕSR2ӸDEl,xi_ 2O!99\\brB|c^bNeUBN~ffiWSq xMT{Pes@Q7/F摋x]Q2xPyW*MK'-vߩ'1,Y6tKhif3h,3S;=ѱRq I<3>EowTTjK8f[)wb$;1Z'Ahɿ)g.؝2F$vkKs i*!Kyc+C=_U CW!\5 –vW$TƟTHn>Ѻ9DojF3Tr5ę/;m; XH5)S2JNjïP=(0U="3zty/ \X{jU$1צM>w?O3b& ,/Ww%['{z=B̓34̍U|?D*dk&5Lz ʷ&gGF#PAԼ%eMpzr/#&p>L;bo8#1yc"/C<5$Ѹdžޟ M<i(py2T`: L<~!~!m>Y&١x$Hom*ΆFw' ;b7NYSXS\휪K3dIS~rɓ6QEzۀYS _HnFm#ڗ!\`6]ĎX? C"x(_W2ljhY4;o++H GqC`l.𭧰ڿ cW9F36 )o~ow&w"W K01`px[ʳ 罇k]&FHFL6V3Ϣo]@?O!IonnoPIzn*ImXahfٟXx zB ,rx97NzM6L~m_!ɀDG}}c`_NLVlHIQ s, AM9&~!:Jm2:\4< i-2ɂZ: utL.>-O鰼ZO'F*&5%OOV-4EffA,XOriŠKT@^:{9f?0˱)ߤ<:(;ja5.r#Q0q㿌xI/@>t^㴏xN5םL̳8SME}r뭥p7mxeQ]HSal3t/ROJ=gVrγ-3c $4c]TzSs$Dn4 Bn.  I>|?k" BKlI@ M!Q$g&F'*KQB2mDĆxo'JDM*ۣj!c猍glSki̘<.R4!'DsBq3glY6x)WL}>|d ׉hs8Q0h1oii o`!¢efFY(`zWnQfZFpV:͠&U/lFV ?bCဝzi9샄$ܗIrw+p2T:Τl#iuu1 #|r424gXprxԦ]5\N̈́\жL$~)i*Ȯ•XnCk2㧛CUȓTA{8+[gWcw0^'uvy|uz4f}XSՐ5ƥ+Kdv*;y1;jJR8莛t8i ^wYqˬ?vV ETg+E5mGkQ$S4,'X<&IaiTG)v;B+7Qjŧ#J*E+HoD Bx;y~   , L , -M JSs26o`lڜ x340031Q,(*-)+.HMfXjrY`}oz#ȑ^o/zxx:qB PFˍ~6vR4{xzu$F)aF!.πx7ψx0W[CnIEE F: F%Gx;zu-#d{FY>ǂ#K#͹I,x;zu.#d=FcvK#͙I`|Px9 }kfpw?.qn}w{CX||v?&Na1x۾& 52f F& FVV IEٙyg1^ҚUds}%x3sSl1iZOgɌW}0Nܰ2t!FɗMd{gM˺ )+^0tcx:n#PFˍ~[OMxzuA&ś3q*u= O,* x;zu-#g@P|cf:F{xB:3Ÿ۷Jfp:100644 version.mkh'c= x340031Q,(*-)+.HMf8ǧv l672[+6;Zx:qNЍacE*ATxzu$F)eFFF: F,PxD 8!HVj ɉ ɀ100644 iprlib.hSEaz[!e8LȓB;SxB:oAyrAxͦyân8Y100644 version.mkȟR $xF.s%nx340031Q,(*-)+.HMf̰+öٲmeW{" /|x:jPF~Ldx;zu-#d;FY~cC#9I ^ cxzusr wyf&& Eyiz .YnuC:1YE鿦l\-oߌLK s227Ykٴ jKtMQhSUogڵjg 51+j.K-*f"p VI͑\PXUH!xq9)j;Dۥ6wLiɽ9KQa3S\v6$Jx7KpO`C &{N.𜩝iL/db|L{LbӳO3O_I!#tLۋ3b^dL1)L_I03?1>9?/-3=>( V/$3?OVКC~:W&c4s2I1irMo$:=+kjNq*/7]; A5 d=`2ݾ;%S_L/bK,v'K`b:xr |5 2007/05/01 21:56:18f7hG<qB4^gtv7`Բ$ӲE@_ AVjB'Xyxq̿7 2007/04/26 13:32:30Z\\c g˳gwakOkU ~ ~ a9-ኄ( 55K>*N~(+GxyN SE- L  Lͬ -6dUjlPZyvZͱٶcةx340031Q,(*-)+.HMfq%C,*--8`x;woLB1ʳ楖o~͸qrKfqf @ xoiFɧXRKҊ7fɸy&cf'x340031Q,(*-)+.HMf0Z{b`˙PeEřyz 5e?om",f}M5 Ex[5L 5& F&Ff VFV'2.=]ωCXV!%L.3?њ33MAc*QE]yUZ@ jj eVc)aGRU19Rn,5F9^'7GI0N/fHKIUPU(NMKѵL6,_8q,55 +Ħ'1rMg,Sa49Yjaq<ĐĢJsRH\d'f$V\홤+jW2>:=׎ijx340031Q,(*-)+.HMfxs8iʖ}x5 :xZlB(ưZ}&I* -x;zu-#d;FY>ǂ#3#9ISxZE3N%v|X:ֹe 3&q3W2x+xCy eUX MgqNWe.,}K'F,2"E?x2 5 F&F fVV'2~͌Iqru1:oyrE7rOb$y_ Lb &g$)h%Ni(3}$F0LaPWUHKOO͋OI-ӀմLSi*TsMQl+F+2ʳuk[3JOQP(8,5>3?1>H@boQjIiQ_֌%X1f)N.ΌOIJadRrbtFV.NN;%?Z} $ d0x86 2007/04/25 16:07:15i]kHchar<TF ex340031Q,(*-)+.HMfh}kkdܻg4>*8x{Zx?̷T3Ss731  [xzu;G>M}iVBfAQr~^Zf^å"sDL Yrd'E*=wG|c#=Kms2~x#a8kԎl+\:ى)du&Q[әk{PMTsYjQqf~^n6CZr"lN]cl&TQa\xʈo%WAxy |2 2007/04/06 15:13:4657Cf;V< if (!strcmpOg_name)) return 189L)void <fy*)x{6 SEM L  M L6dJ/,f%9dx340031Q,(*-)+.HMf.0:#9*lxV|h01B*x;zuC<#obQrt FQ+x]LXHU7͓8S݁euEwun=100644 iprlib.h;MK+ bB(/x7ʈ׆&+ͩ{Hm{-25s ",ho-W*7*nx{y?džFQC#s}}c dDƯNd pԍPhc 40'O T,$20K+3RS JR;3.(yx{y6 SEM L L+c++78˺!B?G_{x]LXHU7͓8/#]$j9W,(t100644 iprlib.h}?7$6_;IBo' x{?džFQ#s}}#CK+K+:m~ྖ-S7Baro y0И v <1PyT LR;Xt,PpNLHUpLI,(I-R|]4uvdrrdŃD7?OaB8{Z2<GxXƳ3 2007/04/02 19:09:48_faa1ed&c%Rd#ha[dCh@oV7!dxzuƒĴw:MT^YiNf&& Eyiz JT{Yڍ,aWVirr~sZ+dGƓ3w3-SbVYs<8ОԻx~[>.qRId'Hkvg:nh>y݂s6@5gf3:s*-"`n5} xdkxx[:l z/m4#R:nx׆ S T`cs`c@fiif (type > 1) type = 0;f@/* xxxNhysical0*/,,M=S p}#l bJXRnI)L" 4yLWXjVk t*yCy{"TD"M5r.48}9A 1]Ex=.> /* 7), */Xd?W003j0nx{ysC5扌_6?p_07<h a;O1ᚿD!('3I/a oM܇Kn:|튘m¼yNLV}SC~:RYO΁yPeEřyz +N 9yþ5AQxysC ED g#%N/Ixzu.g@PgDk^rY1X,11V-54RHG|s%c#)xS|Vz!G`Wnȣr\V)1\&OFC0XBY|eِ.Z<38 3a'&!v )x\1-̲-3mf~8}]9ߌy1 n,~Yap>40NOcf^,?]63fgvB=?3*m׭`^ZZ:` t0eȼtt:x2ï0 2007/02/09 16:15:18Kf2q1ԩx340031Q,(*-)+.HMfMI uԜuJsO=lx{W07=l[7`73O 7xzu;IRܷ;_R`?wBfAQr~^Zf^^*(lm OX,q@{I7eMUS^ПԼFm3?WUzd'HWߝ6bU&?MI 7GpSxzL떴-a^I&v\FiLBJ:9%љzeEřy:c[?aelbm_-Py;t 7}sj5ffvoђ'xxk<*@C c (r٦I7S/Sef+eAx7 s'՟``d,Kzذ}#'2N_dfmUa2A@Lx;y: ,, HpJFQXD'b<[i7Β|ɩx340031Q,(*-)+.HMfX!~f,f3Y#0TlCx{W.o7- .Qx\qF CkI(޵36/:z򼺧Qm/Z)՚r)100644 version.mk|V@H94m^''nx340031Q,(*-)+.HMf`?-p&pz+'w4Z+-oyx{Wx"wBƨ9%8@=5Б9  j|9&N73*jjBd3`'YZa9]>Ql{FAOvd>_-2roي`2G>_Ƭ 9 "|z2ɻ$52xb|6 2007/01/04 19:11:360Qi_c~0X@ן k"~淐B*,I%9xs88 2006/12/01 22:21 16 F\9jv{̠$N6+q. / qʩ2]Q+,x340031Q,(*-)+.HMfuwP?5E*X.}vID7axV'{B(ƨyqrNz,x;zuC4#Wb^ibQ$F=ix:m$|$ 'Voeu .>I29L[XWr&tH x;~Da-̳wOjf4 ~x340031Q,(*-)+.HMftPŻ_n.k ljxݛmj@=(4#xtϘS+c |iJr(g_FAp)1}:aYp <)MO$100644 version.mkz;9͒74i x;~D = J1yJ{9%:sSުx.9H p\ GvV+h\sx] 5& FfF FFVFV'2]۬xur@/dL^2ޱ1ZN'1]>#d{-~0 M7(8]}zEFo] c vˌ7x_b.q!#;E8g/ӥ~0:Kf)h(fgkij*TsqNh5}F&Nz?% b`6LM6?}h)YSʥ`4nQd s# Jxzu;Ȕ§.oWε{-&º\EҌ--}L| _&Z݆f&& E9Iz ቗ze<`ٹn~ieG =ͻjr:TsYjQqf~^n6ǼMkG H+KQ5ҜBCx[HTQY{;3j mfmԒKETi2NP`EԉVEye7R +{ ƈ.P΃e򥠗s:{L>(ƱFVTK|.o-ڒ9X|&hZg:j 9ګ{7$8@ (2+b9@ӝ0 5a]ܸ t 6|%k[hZZNIjd*V|cHM}rXz|3H@Rxg7\xp(O:٠rϮx\/e zu xUr`&aVvlU2GZrQ HXlr*#6iMɒZ C!!Rd8LR'-ʝs"ڂo;&ՂN~y"jZZOW\̭-)R[N{bebRDw)+,dsaL?+F&Ϡe:pL~c}I#?|'ӪC[v7_"_HZC$!~& QkabdѺH:WdQ%VC^*|0Jt4JqQxSOIwy5I)IF?#ԈMKM:F\¯Q+8yr=8Q|w& ҽ)jM/Kt)ZB4F. űkt1i|=3K)~xE[HTQY8qQ,%u^.ZSDZ8'glTt!J"czy ,C 0f򭞢to &>ȩ"nr9\5Y[uWkp)1|,yjRIď#NJLj)YE8- R$4dQKYp.وi)Df߮@k Y&+hYV]n> mdV7Vlbu$58Ċ/VzM<*Dou 82tP%JI$%-(Q2YνDWy+Gi* #D$@/~J0(BT8$-b4a ?G+(- 7]݇32V#į2/@]mdmSB`m:NAA`iqΞ^3_gU/DB<̱ 0aya,JY<4iK`r\C3Kcd?Smxy SE ̀L+cC+#+M96;qUqm~'Ҵb󔽷' | 3x340031Q,(*-)+.HMf2׍˶={ΡI 4x=7u26HLIo_x;zuC #4 F+* xzu;u辊qO՚hguD!(9?/-3]/aiG=_oz+׻OXTZ醧f!i R _FLj,)e #8';1EZiOx8;g[{NWvqU&TsYjQqf~^n6C|^(ʲ'cRFxyT`-/O,}Fe3rMw{#m)OYs:Mfy^Tӳufh2dx{@~-3K rJ2n{Ȥ 55e)? 銓dߝΜ%+)&VC0O$Yj;Lfv*mxӆ &k665-dx;|džFQ##3}C R04220<Щo5/8}i4xy SE HdJFCyļԜwę2S6RP҈x340031Q,(*-)+.HMfc骠 [϶Ծ[Ɠ4lcxݘ+<:,xzu$F)eFFF: Ff;x] 8Q&T@XXSQ窎'_100644 iprlib.h5 Wi =~BYŵZO[})Ie8/&U%,ex9k+džFQC#3}K}#sC3+##+clp9׌-gxH5 2006/09/27 16:22:31At sB|CoiG X"x340031Q,(*-)+.HMfx'X! wM2; sw.^( lpx[:i* xzu;̣jצ|> w {&K3P+aoWMoBfAQNf^C!ϓiiSwJf.l6Ĕm,83?O/7!ޏ}63-g7;eBix{_~-s2/yLۗset@@ ;uz_x{|yFQc#3}K}C#C+##+#+/\T  :x340031Q,(*-)+.HMf7LKFGGҏyax8,&l4xKژAms=["hxzudL>4}^ cT֗Gf&& E9Iz 7'j;7/'XvzSi̫H C 8O* \ZTpG4R-`zeݣv/k^2H6 !x: Ռ Ff fVFfVO`d,ܮW5Qu Ff+j?an[6y-ٌ3aԝ]pFG&Ē"'ӡLs>saQ,_X?=* h3~@W?ex340031Q,(*-)+.HMf(k sM;EvU}.=DxK0!`c<͏ٴ@]&x;zu-#d;FY^ҜJ#S#IxBx:,!8eZt^rPPL7q33 Y[Z&x340031Q,(*-)+.HMfp>0&s0sv8M xK?!`c<ɏ6Gogh axW'6;T7aäeߓ7jp3Y^[5&-f6&:H1$f_䞔*PǰZ8^cKiWY100644 iprlib.h@CS~XgMx]ViBYWƒLޘ;TMM&5 JDx{@`^ ⭢E ~.!~Z9¦F jj@̴=j;AfSe,{fxqC1vƟ#E7O 9Y'kxb5jC^Ɠb#%UKs32K4 483l2RLMjz, xx{ Ռ FfF V&V&&'09zwtdƴLw$3^~Qzk)gs0m(ω,0Դ䜞`(:=K"%LV!-3/%LNKMմ>.xtf't%?r3sOY@}x{Z(ZXLe $ĥYPR[d=c &yLZ< |82/#xYr¹JI0Cy3\-[x{c ՌBf FfF& Vf{l{V98{یr$x60 2006/05/22 22:41:2< $ 

zIHc-)V7nD({乼fNf泔PP6*275UXQ2ߩ/G-NP<r2třfhX4NT5 6O2D]Gʏo)@)-HI,Il\~,sPe I;.iphiP Rj{>{Ǚqfʶ^E]YjQqf~^n6Noy7N[ZxZ k֫XD٠UT L}:m@0tp)Og100644 version.mk"z#Fv'bbN*yx{@^ ⭢E ~.!~Z9¦F jj[ k)TTT(hsq)L,Y6[^6_dF{NRc#Ē0+L7̸(57,l P>QVAXS-1'38ؚ883>%)]#%LGA)(X!1%HJA5btJ3r4@Ejpqr" @|nQJ0 lg8}U훯2NOd5S_,Nmg9l꿽L8?iqxW{xTff]HLBM d6&HH рBX67f7`bQ@^RhAmAԪ.H6BKPR@z&Zsܙ3sf~?/=!H7JγXFXFyy#s rIkAb3_k D!KV}R 3iĒ|n;ԟ lD?:հOռSZJ1i!WuLzR;uU_e56 $ϲűNK1EI ٔ3)n8:h#Zf쵳}a샙 vil,U'הLf]֡l5Y e \[vk~|Y)͊\ⰵox`[(}'[+k! R7]t6gWsO],eAUkZݤW;0sWzG"{ATZ2ٮq[XHp)rJS#9aᏥ ]7]WŽ LWf(ۆ󌪍2mES7ԃ]@O;Tz b-Vjs8gSTmG2^T_޵o> jN&`7ߙV{]#~Q'~F@`NGJo [y|{|R,c5稛OX$6t|֤w[Q]pթJ[.ǩw3IR_ wB4X(oi^b=|y~|}(/wKGGgFCX 8%|I1]0XرX MZ*Hܗ #=~Z mz;J SPχN!& A;OTC$Q1|C'£ JqFZ)hAЂt0ҊQB)Fe04_Uz,;ikg0*rXJyWn["dr(T5r( @o Gt3ܩfqn0N4Ã$WA(Dvow Ly:(, 6C'+."h0\j%GN|=^/O+#=½}lTV ]/!|`0]bjthRgEV?d2CW8רe5MduDx<ْ3<bppu:].Ev6Zt 6M@o!u›|CP"E!FEPK`DK \z6ځ5PÇLpz:tg} 6qzbs UpՔ}2zRE= thZmMJȬ"mkp)VGeA5ç2O3!1MCC2M8.P2HCW~[}5"V_} VQ|~KP/k!M#ZY=d9ȇb27XwFM]ůxUqhI7DML5gcBj;lvŜ+@B,, h:ot#}ND_wT7 sɐ!V>c8wc$LT%^8QLTX`h}N['Exh\ .Tb.l&WRM TZ~k Fx4҉rGH'E11pI;#֊VlfYs6r~>3~5WZZr.]@9 {%muwdEO+1j}8,XA6ߛMOnǮ>E!S9 lEO<7cQ<7ڱWi~_ԞGU ~ev_(F㸕6/uYZm>k(,JySlģ йCCV -q ɻČܶ ˷:m^ť&ku)k^0A)wVav*yXS P'b->;Yu[z~=\c.w*R+8|FlZnX|qObe.ڥN<F[abX ľӪߋ~ _a]zTp7 .>T%Ziy%ۋ"U_hoځ#8fKĕ.s01n A!*nǾu\dH49Fco-ެGyS-btG!qA :1.F1c%#h~Q:C腣I#D҇"Gr'}~mȕU&ʧ6+DӬ[%ZBNmMm`yjiwYN=l95pT0a5a8IAŋ.Maмn ~?,?#W[S{01{^?L7~{S,0?/pq4#učy 《O>]Yͧ F9S.Nz;+">< 5:p,Ty縋^Qh)Hb:iSпիU2xm]LUse|B; ZXv*u;2ٵfY`wq-M|&I[Hܜ{9O__Aj`|]~oxy1?7cۋͣ15`J| EO9,_[]@[턓ڽ dI{4-)7mƖIh7'vBKe'>8{vI!.D!3иytEyْ&"ɼ(p2x$&D,[ stGrsaJ s!j-֥X(YE.dCjpvgM޿SŠ~QB4t03T3s4"DypU2Jo{ I'+NB1,s3oB@oTB> fwlf3Hg&Ȥ:Hmn0͂ˉpAᘛ9hނTzt؋]9-+ϦA@K.7ɂ1 n\~.ӉL6qЧz GrvZw UIx340031Q,(*-)+.HMfh el~Ur n "+Z6xs9tB0ưy\! ^9 Ɠ T& 2Ni4-|{N% x;zu.doF.v0W[v^9 ƛ3V )xzu;Kڝ||fi] x{W^-Wů01v+Wt : uxzuBV77)%p>7cM쬳;g!XV,[zC3̂t +R$=s\߃oCV)Lvbc\yS/[Od'H4&l:Z2}[%TsYjQqf~^n6f_EQdqC)ލajzxôi06F{a#xV}L[׽׀?`3 \a>vBC!|%s'`سI馤N;.DQMR7 ]#C:4l͚j4L]6UgMg=swous tS6w65 tXbw5`ݎvaa얣$G c*VL[(SØsuJuqBUl)(i!JӶ|6.VPE_Ns(d EdH]8fG S!ܢ; Smǯ)W>εjimhlif'L*QDP"lN t)b4T*+yj{K-}48EZSO׀*)Sc筠(FWL&/kdP#Gpg@h%2ص[ظr$h'fx(LI [6`>M͜kE{ʋJ{c姜0tkxǻ{d|CrH {3wpUlOD3#kttЍ)YXU2ۨ3u.(82{qf.X}y^ b3'#/B0]4 iG?;y)zK_4f=R}?d2;zl0轍{)vmB$ξ[P79Ci엝,nmAvllLnT?WP )[k[(aGÏ!izw;t_`܀2xɬ2Ch<:wmcj",[&>x;Dr8EsYMmC]ʭrl"*78"nN!K_۲ ce<.dώGiֽ3tfy3? y)19<ԯ4<Εj5qCKGnQBia▶cPM_©QB\UK]M-muJ#ӒζW]{{KoȏX-?0/fݤJw /XX{J0 WzDTreC4VG +u:h$4&:qZ3Bq꾝Sa;\xBZ6f鶍Žv|86p MWHD(z'7X gɂ'&~ m':sRwgPh^w#kc$م'𢓘 "{I1ƶk7mp\IlR# ܞsR}.RŸsh'˄K.!sXff,xJԒT\rO)spLC4WlÀuf#6%DKܪ#W<[Ozi67 j#qC/UYU͹T|PXD7 >M \;ƳO]H4L5ɐdK9Qhpnr*sg[F!'δAj%X~\;)yg^@zI3:H%{SLR`}i+|Ҽ?c dtCJj8Յ2MWZŵeDwi\^$#(= 2 *ɗ^M"xI$׷JKE:SZ10hxkt ӆ3'O0b,%l6{ -, xe_HSayBt[Oيr,Ua'ԍP2bjm2re|Q!nxJ]PwdA]|/?}-gq8D ʵv]n/൑Y8ǎ`SmrGUgSե% o]Z33 MgPzgrzli]@P>&YA|-ߩb=P"&#ʳ laY5|Q)9l򓵻jRa,Ni4-l­+4wL&ӎ%6{Tw+[C"XBRS"ro_qp*Q,XO*ɔBn_ qbv#A 3ا )&Ҙ3.&FLmc 1yԶn#B(x.\֠KI/t2&4R$N#?ܽi#4@GsP?' 4Ofr_jVBُ9*بJ5}A"(ZeǬ +eb4[E{1itxļa2))ZZXYl^3YOtsg8'GMesoV*X1y2fWVjHbx340031Q,(*-)+.HMfX$Nv yWV\0@xK5!Pdc<8KV}x;zu.doF.v0W[vJ3z2 xzuܐ{JrV y8\~E NL xkobȼa 敌+Y ͸8'vn.bOms@vCMuxzu!&a7Vrː x;zu.doF.v0W[vE &3| EYxv\[&|װ589N'*f7Z+Nm100644 iprlib.hܝ\Н;VkTBY19Q-k T&"7lxWypSݕXl${eXmԵ6jdT= 4=B$y>J!ma')`҆`:M4{O9I_ьV{|oߵt0uOO<0;7h-ЉM䊶;#n(,>ݿDVHM.@3QS]imu2jBZL7i" }F']1#4P!M=tN}/yOwh%HI4 4 j0l/7|A/U):YYyHFpˠ"gs86v&]'ͽ(6AȚm&8k%SCs NZ(Zpvgؤ|km^S౩^+bҋ%n{pPQeuQ oxN.22isLRxj>VtӔ~坪r vfn1Bm^mY }Shxϻk20~F$Z.y(o/ozLj Zmp?ld_h} 4c!ZsѸ 5CY\E'3\HF04Gf܏N I.  tCx DrTvB EU&>36psSgL:3J[xx?*-tڲbt6Z|.X=.;pATkك)BGTH\="B玠$ON¹G{ ,8U(>9l>g^%'팛H*\ᵾu)GEKIl-΃?D]b/ʅ2{zQ&f5Ǒv,)G ɅOҝ:d:v=&:X jdDX(7^gj9j4aa-Q)>AtEY%DMC ; )J%&0dmFm6>(mi*-ZTQpȱ{Y{yQRƲOpx괪۔ચT,W6I+My".0n:)(}Xstc m? Q^=ROoh>v ,VvPqQf>(RZxSW$VT9P"pg/aCU! 6Bu-gW2$}W$¤Y^I6?k8[Wo`+G^#]Hr"$^fUvuw:s]ʼn!+BŜFz2a-o%&6saw ugC-[>TOhl`ϗ_[LȎpf6i$>2Oxj JQ^,mKA: oV+uIۏ&V6QI7y$ S6m4byS vJ`IF26U"M);#LZ-VNEz*ei}B;ςbr\ȾQ[ o$Z#sYᑔxS=\=FxHR֟|e=RgjW̰kH(lt8ۏJ ʡmn'őqwq f%=n66J->giKOdZ I住ô-dNHI]^r@H,Z/ax.b`sUgэc=\ϖ= jwKu (Ӫd(3DMu*\ov;չlv2>,S0YJ']2EРPWy`w !OG7l0ID%q3s`h Q\^͘9DkYj [N'lxkobn}̴a 敌Y6_b.69NNB$(=d*ғOrON`bF̼̔;1oY'Ow⛜51hr_R(Ln?9~ F&+Mf489cYPXZ[V?p̛agr"^ 7/ZZ_\9y `&x{I&-.ĸ%y<ťy)) jj/v Oo9]ƙtz#3+wQjqinf^:4'f,N̜S"ݩx340031Q,(*-)+.HMfy|nGMBF!]6wl|x(`mlYJ-p'xvECT,Ifj 8D"EvSkk$_\ܞ100644 iprlib.h>%$[,JݏʓBYg!f6՛ßCW&S6i1xI&-wfj/Lۋ?0FxC SE  ͭ L-7dܲzsZI-JjJx340031Q,(*-)+.HMfr4Suz*f0x<`mlƛQHL:xt˚on#M _nw:4z&잊txm}:$1y74:) 100644 version.mk6_P^5DY}sL38xIuG489342 25RRt25yeb#􆣌\D&EnqH1*hfXg"\y)ey% ]E&7xLg6]AufN Ifr[isxZe,OOIJט{ѐGAX/&OIscNۗegV":].BZ~E_bdfP .2 ")cnc mx340031Q,(*-)+.HMf0z\gg,wxoB(ưE3Gx;zu.doF.v0W[v\^yE Fƛ3 6{xzu}@ҴY\Nc exm{8 2006/02/09 23:12:31SU1ipr_force_polling || (!uevent_rcvd && len < 0)) {X?X!||}&$x9 |r><$f]S0}$@!MZX(c9Z&TEDx[ Ռ FfF VV'0N<'d+&b³O\ )iYZRZ?yDn jnxw.dx(8x340031Q,(*-)+.HMfp(ZSrv~۾O"1exxwB(ưy9*F~xzuBV77)%p>7cM |H3ˊA&,340031Q,(JKLKf*':ŦjVosS e 1s[uƎ5BYJȨuS'*mjf0(z?'2.}kI٘<^*+81DhLZoqzgߢ3b}kqHPd'mɖݶMg˙V^$jb  +zW9O)3wfgf3|92a[Ýˊݒ&/xʺuח{^='q(WEDwr?u)*{RD!(9?/-3]/AQ1:fxk e 9OX7ah%:0ncΧe)I '?ɸR&FiwVVw X+iQp'b~E }9I@ԧ+=qc2]̯Ovb)MQZaMGV˞@5gf3 ~ߙG{)xOx_.-+no-pointer-sign $(IPR_DEFINES) #CFLAGS = -g^p -lncurseZgEp3 ox"zFt #sbN|VN6x;#zPtC#g@P<xOc|9)iAn6R l/x;(ItCTNg#Hx>l u'{jn~˯V x3J!February)%.Prints@JfF*lAx;cr;d'( 9x|y|TE:Kw/7 IgeB" a M&N\ o޼E7=ꈠ̈8>Ό 9-Niau^Īɑ \&#I/ ƚ)酬J֕y}ab>Ht/?Tn w q Be}=[|sjE_h}e 3Tii[qH lq_n,רWBҹIqCcXyIJt4R()bHDKX!M@Wf-։/`:Z឵R)νcxyg!fJh[Q"T;"bO"D9WlYm`A8X靬bͽ-(S>啮l F[(k`hvٌ˻ZZtl3έ58n 5"sϦ݇*KhW8C ='l0.¦<R%;71p!G}Jt8=rxMI'HoMrdY[p-{#ޞ.!o#DCKE4p2AP8>i3GQR2) j7̒wf1s^E)%JS 6&H/#_C<)V숈WSe`ZnM-:>.lؑ݌~^1vr#HEl721Δ|a,;P3U=X-bgGQxs_:tZS{Q!\t`^={^k!l86w]V{3ټ&PXVl]&(u$Xb1[UV61kX:$2,-6#R aZk=Ĝ>]@n:viq}t-AbR}I*p&q8ԼJ4v4ʸVVX $ǫ3-#jZܹV|H0ANiyԗˡ[]$:Wd@^]}hj*=&d럊 G;g: d)ǙaA-51:2ٹLvZ3HZDxmqpLTY8QIϾi8Nܽ?Ip&-I/ M#>'>@X| !b[hk Ă+G ́n>{P*vx6_xqbǓ}"z/2UUƈ%ONj?&>'zsևw;IjhAi7m]u$X/uR~",<7 ӏo8KϽ>TX/N)0)ieʶNH~jgo޼0Tq[bQRSFj66!H𕈡ic#|7g)$}j xJk'[(8eZxJ 5m8&I¿x/`j-8L!\ ܒ7lo(2v0;ʸ8cO1̾UTdgLtUWMnJ.$b-

֛?"XekP.$NKX Z'|8DI"9XT%N fFy HZcsX蜄2ĂOC<%ې*kᕒ^)T@G NLjL.<+S`<G3ܛc`$&\]gĥc?/@;Uf/쒈s Iz&/"-vR3xPu$ Mv~+!&-Qd@R~U܁.qe+Q *hdN{f)ɂЯeD-k\ )G%UW\֑+mKX/{`^ȻTcƻJ-Sm7p;@̷z$KyB(\́m✫^h(WsW[@ Tff͟1CKл1|ϴtt%#KP@e(򥟳VL]7 K y<*, pJ!m80!ԅ=$ E!b5hԍ`w ZKDŢx9wPgYz0$a1l:V xbPrDP:FY xJ 4qP&f}Ha&+c=p' DOcW > AH 41+ Q.d}UW*RT!Us·Gq@Phx6p6;h6LzYRUAi`&#*=@h5aTjwH7wt4z*^T"n~bTE/!\6Vgذh!Z `G3웊B }m |m)iq+0 fjΟ=q^]榹Xʮ)N]lJ)WLtVǙXXHwdJ*D8gz'ty'EsAUx@Auu{5Rzq5-j:G@p ~gRu e39څNc' 'ER80w\AV9  (RghN;(w-J*Zt:>jBK= סT^vxG6o3߈V(.Fys#(ozʦi&JכЧ*ޭt3oA}!vbH3Rpmi+ QǴ='P=FmFp) ,TPvɔl:mA u8s1ou8-:$^dw4@V*[mEAi؋Jzk=@ s{2Ćzwenϙ_p:n4S T9ڏx`qĞC S¾@2tS<EX]8 6\,~er>Wg&~t-?v# /GmB:79Q䭧y=8}h7H:~'(3?G pe]Ԋ9'WǬVh2bLo?;}S ;-=WX<X.'{%vl -r Zb*n"|J*lMJ@t'RnzXr4MpH4Cxu|/$/!p^}}F&ۚ3`L Mxfk^0 T+ymAz{z -}H[x4#y0^& u>=[3[F_ywp$_0+7T9!ǏSm!5^@.k;K+-k. Py+/jqTA![_@RFn2%$礠, #?5uYR6Xì7}kj˩|]2#/]$j2j39W6k `r ItG@GlmRxj2)R}Nw'{Qn֔ s7Ah3΀F1iJj$SAY,q3^}EA!ï) V~7Hl\3 ~AE ""5}r,RpFI)C+x; grV(+?.@Q9ܦ-rR+U lXC傏)%'2%a]ghqM,ݒ~XX@i0*Ӭ2m :H[KigPD8gP?9Ң 3(gA_Rn9䩌d*Cw}D nAi=<$gvmFmNu/t[NX܃/ x%Vzj9hzJYIY3(c5`ʳjpՋkɏ4zHou2w)Iwq?k׌pa`ڂaM(_rͻ9B0E ̡3)^6X)O xNӠ `nTM\(V~pRD[+8ӰMAPeo"~*!:~k! 0)w$03H2xYSQ-o|7N Pr'gxn'Sݳ 3fK|ޅ2*1B9\O7v(TwD)8s7U*o1쨀ÎzflAaq$^g"(Miω @s Q82Ǟ) r0ܬ4< a܂2>OB@w#5а eAf,X +giSpT8d,!YP<^!6&&z"u֋VśBJ3qlNr( gzKa epg R|Ϥ|Bɹu~6 yrO䯋j*eAXJW*V!2JI18 k9PTQ6LvXҗp&K`seS2}$L-l`Ud6 Rcg֏;Ks RU)ޤ\8 Gn RpM\V'm m~1L]Q3%Uf R:tl\3gqgFk՗FIy*^V‰|]H+ s3'E ϩv(VpG9N9 T; hI֘&9`"$ 18&W3yO+pU NJ|ac/PZ`zN;vWtw F B6+LeZDl'qo/qp>p5|}DRx݇̚T&qpe:et.P51F\G`_ܰykeKIF<;$,KȖKӴ_dyW'Q@+q*;q͞BM? ɆOO*)eҳˉ㰡8ξx v<(n`^Ia;#hNkGk'f|L5&xQ\r00GvY?A*b޻b }:N"-L:eS\8!x"tNN*/=RSuZ&u1ij)1N~&^>|ϔF/d`wV_!?l3I^z^AaaHę/)ؠ?: .BHoh2Fǐ )ֳ5`5p1H0VL^4Hpܰ[ԥpMM (5fy'w`nG`!,"VYxTJQ]tsIK2aT5 hx(NA}H$A^M_{5sfCr[J4<>r`bI/N<,S QY(n#*so#E^EowRъbJ"hx&$>R܁%ZGbآkb'л${kE &_+el0`^*J$4cwM7=unOeylz)E)1_ofZUJAU}1(AܞIS`N Rr# y|/4KٕMYG.iEp"MS,<;X$*Ve|G=}bwm?1aYcu[:iP]NSEb?b]v3]j~Mْ@]/@bVv)´(܅͔.*ҷ;i53=I'(5x gM8xadHxUkLWι;.J)z彈JUTqwQ̒BolilLJҿ5iM6})6/ƔiG;i=ss掿aNJCL5;(ҋ$:l\^0vD1؍1{}%3 JZ xp8,nAq%q&C׉|sX /4/`5AQ= EVAָ]\Zt lsZ]A.Gˏtnp)1' N.Y}R^ &5P윕fEvz7 ά 9 Ѹ8G&˞(MST*(tuzIUCQx-MC- 3)j_,w&Ak7!;: ua^u1;[_w^nuM$Ystb[`1W4N_EEW>fqF..x)(y|DEU 68Ȧ֪lJQšRPKy9Ѫ_2;-L6]ax390ȑoNl\aǟig2,ƯRϬ-ٯOWiѯŸd+$䠓MY=N|*{qD !ҘLH;ņ[r. ߍ, ٸb2Q Bnp*r\\t|$832ЖStQ+nb1)ese755wa-dCt;VF`{;0,behejz)묔5R EWB2MJ[?떮Wxd 9nIk,Y;lbncv~cڸ}=*˕E )~?>Vbz qx,2^^szE-2ĠӑXy)e>HZy4Iv;F;RB3OE+1AN[h_PeVb&pf.Ȫ!i&05m Zz51wH9;!;C\y?I}MtNS6,&%Y]J -+I*7a ]l8C -4\JqÿNmV.xqW Ռk&etfQ00|1eRL@!C.Դ̼Tpx7Gwg/DnE&yL@YH1GI+5;)LˬTuY4ZYM@|pcHh \0&j u qE}#<]}\=\6H0( ([X6p1o͸YqRsO~y!f[Z $e8SrSKSRu)ON4PQPrK̩JUOWҬݼFrd͹B6+0r)hit&Sc4U٘bKeߕ6o2g6ID1(5$R!3O8U/&OISG螊 {s&?5䛶lNY{7H^`5AUOV<+J 쭄w\s3N/7/PQoV]Ɣg;7Xvds%Wl%ܾÁy>1'/ϱ9{F}6gdFP?3N9ys6F.WAxW 6 TqxT]hUfL mhO6lv7f즍iSkPIX&3wwٝYf$H\ bKA8 G}2 >R|RA,/T_Du"ŗ9wMW}پɹD*Hl6ΦfͱlbϔNʎN`.G$O@ Q9iߗcw9.у}fN`Bp/o|?.[z L>ؽNVբSF0©dYs,9ydf*h%Ղi!8ߴwtsoM5KIDmfZ\f3_ j$VɒD *Ue)@Dv9b3Boy6[XL Цb1h6\4$IĬֽ$ 'D!tM!k D/ Nx}tc6S9nx{UrԔ]w'݌|M6~pEbbc7{D;j^W$cPU,ׄ7Cx' ^czb  ŗ+(>'B}>la:|>NUXy T}x}8,;tw}&f@}( !{wדA J%1|0^686J4Z :١A<׺2*VQ!,+xyyΎ`(LX;)\LiIhy+Tc@l`H&3S^/+3,~]ś\segw4k%b A:!UZo4u>Dۆi5 @`aD àF ~XWDGv,F; ϮH1v@ >ҳxS+'Ϩa2 w妒8o :lǎ=ێ ˚(#-G!%Z1x_ϽpႸ-6?z{ivoqk{+kjĤx)vttⷫCx/{gH`,2@D 㤽aCfAxKhf']+K۲-ǒN}$#{BX-8m~v2va7TV=PFt,;N_~xs;-g@B% %2s y4~:Eёi_ssa_t䧤"xr5啫D>@+YC]!~?`~+TMݰ e5Ark*Uի5:0mo|O{ xPdspx!6̉@?߫kc)?{] ܳ"q?W@' t0Ts+KʼnQ?:#? ??nlbp$7xߙYON‘p'9Sc``a$~4P߻"Hl@96˻)^8iu!e A$rPmD~Y :zOSgzYx+-t6081pos oLu!3a4X{S uAR.F~3Qkdr7LVe΂m䄼[+"%pfot@f~Ia86oNd>hQySСjj; dDe^4L-"R%$ NzI`( R,$OQ QaZf%802G5 S֮nX윊F4Gq=5U[$YKf,mz#"$ywI@ULr`j[8 ?yAg mCh \f!j ecG-y`;̔k1~ڸ`L ,(ԡZFWyֈ3C(H@%Dtb r҃/_h˴ TԻ N<2xoL60Ea"5u1v(묹qJ`mq\-#~q^w-vQyVɊJIIalttDDjj1côA:WvaU[l R{Q+U]waH?5M4+枋KHB0q orEJD&8nnw?XI͵,WX 4f2!%?< Dg}lE%-7RkHR4k՛N$$nh-Yuw"x]nPeJ\SKJ՜`ٱ!RjE!JQP膘" !y)P&$6$ۅ3}|Bٲ֪Y^mZM Ng v4IMᗅsk+˯KyȍR:p?zwcCX^[>`i"! B6Var5U\ol>y>ivXE2i ƫ)SPfa2 ^.^Es!V smw7P'ٙ&L ,, ȷX;,a=1,h (]^7vK&[76g2Y9r~Uջ@.|c kz~ u]@e7175/Nfj)f]7 o>9udnuUfzTV,F_Z$0_bs"C[(T,>ɓRg nfOd<+l;%!^1}"% v6g_Lbjel#:GOs{܃8-u-΢P6KAl#YFOY#=VIt;< ^!#[\>c iiCr Sf0`odfR2Kq Z26ETԜlHV5X7L{XVo| A2. yViiW$/ l]a! eW]mEݕ,+*Yw>YcSLYsJVrF\g:6-W]shfSZǚ%$g'{ o)djG qt+0dC*p'zۼbem{[)8SNf |z>6!C]1~pl7wCQD]8Cu +X|>CZz$22W,js VwOF;t^WQȋDͯ.Xސ@>V"wvi\v;ȟٚgdh0{}aAOd/rDWga,,yQ<_S(/<=ڳM0Lulvğ\Ń\´JgԒ!5Tz*k#]P>/r` C RCPzb r)*!n c EL;;`(MB=DbH`O0rc0b| O&eⱪ@^Iϡ됤ΌB_x}`NZ}p?VIjR_6W>V{!=D=:;`_XnfSRWh ZSŷ4D0U- Pbϖѭqw7\Ad1M[sjaW.!$2J.6Y3*u[SL&qDѠ@Z,6OnLsƚL&Z`"(=C{$MNnLpr0q[3RSo}Ŀ[!xQGt!؁ aіuDde#0(zp2ey>02lj?pMPW/h,6~yZa(bi2yGޢ?BG8'/B Rv\ : i%% *o%: b0z, sQ YlģH}8Dv0^ wO,~_tiωxOLȮ[JUђJ\lZ1F5 r|peU2^˛E*G#̧p8Kic:FLэ8,Jc/ơ^pY9f Mg0~*Nr< T\ *źj: 8eZp#uU8*(VZp'٩hTj&hd\DG'c1M@==dF LMG63 YE,Au6f J+#t a=Ar0چMs$apI{d.҇onņ 7B#"맩5R~0vB5g^_]i,\PuUuU&zcyRxU5ؼ\4f|~^uqrRMmcJXOWnh07UNȏL7X9Us;!nOI>k@Cu]-i5An׷$(r9dաamDËa0XcB/NcLkpBcb(;=945Ӹ ToMP!Y`iu\>$* .]d"i9 vY+U4h:5gÜyd&ܟA qr!͸?,m u B >Bݘ|܁:i"515HkxOtvR|Mm^z1O-ʻPi%;`>z[|_CsnV/ڕ:^(:u'&S]8X{'fa0ZˤgtnH95-Z"LRa6J~+96br4B`:Ù#*ήdZ4v%0 JL ,J spCK@ WR}xWu&z~= zpEo M~R8Vbx[q:s8qFT~x ӸB(Hzq@+t3?~ߠg1=YSgq<= g42`h ^=dcwdo =k1 ܭ|C_`3R0JIY `}I(e׽0V/fm;3w1Ӄ;r+ a0ޣ>okxRJ|^ do@t8q4Ӟ-jjbv c8۵Oڨ~OCzpL|jَv, LuZpjhI'ɣN.꥜s\կ=#i?z&L6ԫf]V]|lR Kr)Iٳ.n=9B/3W5b:C/s{C"7 Jގ>ؿa A{جtzѥr"u}v1]'8dǟ'8hq#µA;Dmq>ngPIʯ8-.P)'qN$KS3϶GdV{j3a@De{&Vc8Lpqae vy4yfM"JZ26Odx.5/}8P.ra2)-5cY89_fD hJ t x%qP eE1ƪs|K]qbe9I)cJ*/ :3:PxR&_"]Dǫ#߭ZT5GvދcE'V* iNRDYX7a˓X|Ćb>(q`R $a uLą+J%d&fdf@\b&(Br֭DjPCA-7Ԛ(؆Y<}:.arQI?֭6f5EQ)]XDYpTDAfpHilD ERY4IolEÂi54,Vg@9hz0u_ӻ~=GcxW lSs]Im;g`BR;ia›ټgh٨ j&>ulUt ]-L+렚NZu M}6MRH2v%{{ιڤ8Bg-q.9Xbs\v;( &f&7$QƱ(d68Q<\̰o^w|]W=!V wuszHD%LYSЧ:ОZ]EJkڋlКC 'g+2 U/O];<:yh4k223,؝8d³0g32eq9)Eш X_ڹo_'9Ƶ>qqƥ+TJ 9FV&Ǭ's;uvV[= BnrXM;rb& VgAFdb"OJ-P=wq{No@jЄU*774DX7MezvjFkjfh^kjgh>֦$:0: 6d%pS/9pZ V$r5%U]C~Y V_eyu$6Q[O'Z Af9_ Y B@Qv=x{j.ʏ17gH-xJq`'#Zp-3%ɛ͌ UT9XZr1 d%a*O|c "z"FÛeJ6aN^P2ҢQ)!"%phl@+0]U8ږ0ia9zbtY^hl }FlcHwQ %3KFŞU^+c ω?=/ fҼW`FQg~}Ҭ _ԗ4USx?="3F[<p`(@-pT6_wUk(ȣ]k?<<V\-DVU[:PjD!k %7my=Tjt=DJP){TA(3WjyؓQ(U?Ao'[ -J\Jt`XP9> hSn ctaBiFrܺ 'O,q)t+4~K*l>m#<7ceFɌ(Qz3'oc1_>t;aBt]5x*|vG`2Ԯz$)f[ 7 HZK-]7l6LZq~u/o(;1 }>9nނO˪I8 W 'эbɩ)OM٩>u]rIV`} k //9ђ҂G D塚tSnBoGX:q: R|\$~j=2,wN0_.\yzmesO6 zzqt ;w?aOu2! ӒAq÷xfz%clN\)A}spLZu,<‡ft)(X>{ $Zy _)]:X=aJ N Éƫ]gǯ~_a^essE☜ԠwnLoKaӛ&]~)!I\hÍ vQuv\ybrIpm8?Lp_xTOA EnJ ":(.m-vH( 4ew [ܝ =he<5FxEc^4^Y(lv7潷3gqE}W[DBP=磝h$⼮x_5]S3Ns}*!lABM&P4H^L4SEm9;+h7J?h2hix҄4F1dt13*9?N,{?qVȠ'j 2^8] D2V$QT2 IXz);&ا^K#59|T΋V/؉fo,dA23, J(*Y%V7 ްӐXCX "AYB$hB97XM/>?c[AtL2i!cVĔ`lQXĴ5"HV݉>+[8H8wùDw&3&a0\X(s`,oAXe-`>IW"#5M5*𓓹xi\?\v;P8|~{h~}֐Kټkk/_mK@)I-鳩O16lv 6k>BwdlEԜJ;4OgZ ;.Ѽ;#&ʥ'/bF,O"dsq9[X6S2XR6Gw@cfaD ƴ 0P*\͖ΩTYBu֎|cB +xZiCɌY&ȖǥYZRZ7ݚsdvx&?c/t2+dN8z ''pXT.-HI,IO,ΎO+HI-ӜGqd'>|f~o~3qsg W )<8٩x340031Q,(*-)+.HMfH P$}nNg&=/ZxQkAGԜ,hLXx+e Xhb$iG֫/ӊXJlg|sf~xu`o?_^U@q?Wn^qe2_Ffk§34cqM4w< ktJʀVACi$0hxV:vJi\Q%|FyviRU(z[EvW3vo%?^Ax;zuFIF!.πx7ψx0W[CE9 &: F3`,Cx;zu=#d{FYnܤ"CɌ <*xzu#dGFYļĢJ#3#I xzu;nl֍/X~Q_8YéES;N9P8c.$C3̂$ ξKװjprbbd'H&[̌ܭ%"rR3r~170]aׅ$1m9<Rxk$е^q vy^흁LRlӓ 3G0 3?`V'x{-jFQ3#3}C}S L,O`Z`y{n*0\oxC SE LL+ 2ټQ߅MK01Ys#Wf7dܲmx340031Q,(*-)+.HMfjyύחX52&x{7{VvLļ Mκ={<E ]:x;zu.doF.v0W[v^yE 3C x:ҠcD< ݋raM댓x|Dg@&n%xZ/е^i ɛ^Jnb. j x)100644 iprutils.spec Weq5¼H,Ml,x{s/-A=xB:+$w\X':hyw100644 version.mkU!z憯A] &=Qx340031Q,(*-)+.HMfp?-ݳz[j=fkN: &>Ix{wsB(ưE+847/%enx;zu-#f;>F%D Jxʺus3W8}>AqO LL2 22^W̰z š,fϻqӆcVz7s2|V.[ Әi|eۀ Ul<>5OPeEřyz W\-\BҟOS'lx۲-LMi܊ۗ1}t Yδy(P8|xw} F F@)]'r_yʥҌO/x;vie 3E L ͬL̬ 7eyJ+ɢ>xZ/py@F EĜd̔̒\jGd(dO_,:?g:o3IN8a'{^0MOf.ʨ}Nƍee̜nxzJf;Y93[;&3ǧ$$嗥_\XRZ_\``hɉKUJfqbRNj|QjqjI1!ՙyũE 3 ,(+J(âjs?70ExӨԼ̴Jte@7BDga"X=B7 mQ{ _Șyq%m'oz)]_vzFӁI+x.stg@  g~x& ["fYLpψnOJi;"V4lZs{V釞2 Mߵ9bzf*ۘAoc6js6DB1 IuSxJ*-|iEE)%:@̴]GY 4 Ox !rgO`IJYJg.c3 &k[nXLVZ Esx{} Ռ FF@ 7Xl6OymYƥY3Sna4Qg-~13ZO>h;o;#{0Lq팗;``~v'/0&C˨=90@-0nqO Fg̘c?lM #dxvC۽2N =πIyk{3DF̹~ì{}Ox( t@~'.rExӆ)" FF@܌ܶ`(LLȕYfD>=Ό?Om>]ZZTbfe:yud)4uRsrKKJSsRK 8K-$'7Kԣš<L''\#Kcʛ ?3(/E@)y%iX]m6)t3+#!)"C9Ld/ ~8.LiSüHĤT’b)0NUԼ4.ɍ2NSDSS!K 4ᜫ`Qk@͛,h19opwn̔ɼӝ!.Ƭ^wOqz +D$bn0ySJ2OnPx340031Q,(*-)+.HMfx|#\F%?W4#5'x{}B(ư8FWp-x\p}h[MOd`ze:Ѹ+v#[QfSJeD$100644 version.mkMU`!o~ )jY(Qs6(6x340031Q,(*-)+.HMfu}/jp͟/2voeu(_Yx{}B(ưErC x;zu-#d;F5&CiF xzuۃ#_;V㊍(d%e%3Z?:]YǰXya;Ҍ-Le3Ygy C}$ = 3ޫv6ݩBd'H+60;XT!xYTsYjQqf~^n6kKC^~D}QN@x> 6WI3n{ax=O(qd]X'7%2+'%8l8haE-4Mң%?5MvD.ӊoqy~s0wE@45.>Ox;zu-#d;F5&CiFsxrC/s^Cqt 8>y[?VjSueL100644 iprlib.htY!``lX[˓BȆuwώ;/Ƀ|6Ҽy2 x@ [ ofܾMIkB>ӯ3 ?X´o쌆W3)%2y62O߰Y`.f@ll̎0h$Gug>v.UO(N7X\=]"X+T`~|qXB^Ux={3 2005/12/15 02:10:07=LHSMaQp`uKx~i- , L M 7d_ii( Vx;zu-#d;F5&CiFCxzu;oz>%wEҌpx@` '6n-Lo 3,üvRm:9Yہ 31MەO1swa^f!_[Z\P\VZ+kR1px8uŶ*3WʜI";}U~-ƍ mYp x340075U,(JNLHKf(X$r* >e)y )1}1}u~+ refresh();.VwVVc\e%X0avK?y; @o"W@n ^'ԗzK75J|xD 8}ƺK:jTsC100644 iprlib.hSiB ːR B)!xG{2 2005/12/14 22:29:109Oh!SeCvx~ӆɌbf FF FVFVFW26lNckii(YZex5 $ 8rh_V1@+Q38%άta@Vx{K Ռ FF FVFVO`d\"YXkW__R```L$x;zu-#d;F5FiAjx?xzuCȪiUV%F鉾91LUxqC1FSOYKK2J3RS2JR2rRSJs 6Je)h jrqrm.IlN_/(%)} FFɱYZ3JF*(&gU*$*e&Nh!9LMt[CMW srXo.4W//Y4y]f%{HS0xzuuŖ+~Qhw%gZk&$vVfPy@AC3̂tdW= 67uT|Ҍ"^K?⚳ '1.0IMW,qx08'3I/L{6ݞ56E.ݭNLV1s=V>5ئ  (ol"̳&лcP _ڔSFXuzz27n;aIh`K.ki$PTUVwx~NBRTTT(TjԤK[XW3,<@w $ ċP8mH 5F,hpX0,pDc9_"5V+^Z m\Ιyy؅r`7 ȡR@ߢZQ&?kVtlijt}%1 vc2wqr#2wuv,qBv;'hebYxs;66-De@;SOTC<}36q8B=sՋ")LTzSLQ:-alBFȯp6|%h!Bn|]0A&=sag366gl8|bTʔZ =Xbv|'3=fB1iC,fw6ݓٰA;f6O$5L+4:=xAfp+WkFIrb9QS,L`~l@:GƩWlc[Xx ,6O[Ah"e$g=eu8·\$u;vH8qJI!VePV'E0zttkn#2ij %a\ ÝL4Vwv NBuXi83J>33qe Rϐ Rf 7esh=Ial77 Sl-"v.N1sYJGOFEgXĢƑ[VvοJlԚ 9 *oQbk;/kRXf %^!E%qQ%vhگ</v;1]Rr;P+G9[ve>)5nJiR̴("̩-9Cpymzߥ"k9& 0!xO%6&Vgm A" ;GS>e+8>t17K 2r\:q7,|Ҡyw9*G;mo[UII1 p]N9/7f4b}qyϑ&}<|m$V} 1\yN_@r瑕Q8ŪSaȹEɅNylRS5\Uq,Ʌ|\H!v70ѨвvA<@\x=H0"EŲP+ H d8s PUH^Qxh=81Kng qPNPx<1E:(-pah7 i7$6@!R[!l+ _&!S?j`"o?@Pd5a\:t#0¹$2G)hcOmP)56^.XY Lqrg{U5yC"'TmmM.-ڙd7;- 9,q7x:fP]~jIg9>~1:8)ynƓB{[xy1-'a^ryœZ̚\O1NPȩZQ\RT\YP*hi%U(d(WM\# W}FLbrIf~P-J (dOgteTOIc:-70O_~szYFaR ļk.N@rqf|fJ|Z~i^5[Pg bw1qzyF=< .N8P3JiLⓁhkOw(>yF}9'yA;u\jsOA100644 iprlib.h4,hCp 9eB72*$MxyY-ьy]t2u1*1fN/cLg~=!jFiĢJMjZ.ĒdĔ2⒢̂"WAK hl^Bqd5@[aS(J-H$&d qȞʨ4}b )0>g.`¸)NGxvU SE L ,@|JL҂M.(H/RTU0+2@*JRS+uRJ*2lJ35@JtJsARŚ@Ymm ښ\\%E% EC'c߼ʔsVjl@&&Onc/JNګ19UBUAKS(kX*(59(e\Qd]LuE2 "=I% D1o|9yobs8IeR 3n3d&Kqm^,4|ۺ ғO.ynx9D%HJqͧ7FCDnoߒyRBsA$fowJx!UɇϷ;+,Gx8qC1fYr x:>hɏ5qQ[7C>I\\ƥ (cfo*R0x91b`2 ! > CxqCFͷ7r(o^g 'pxve+fWn;G7C>I\\ƥ (cf8|yyN;B90&?xOhputuNpv?J!=t927,Ig m_*C㉨+^d" *A.BE/d*L<'5)D^~>DFeH҂{edƥGdN>^zœ u8M"y@JMn8q$} SByd]1U %{*$ux"1]Ns4bDeILUI)#YIޔT&'ԿChpc/sj@Unw9l?_8pw m쮚t a[+ s XFd$C[ AF2Kൗ@O Z^w E<^Q`Z)ɾDd07"}&iS2J)ʘDgTTs$[jT ܪa?jH#5:p:F M?Vq1o9D$ge2V1I0}!?AccVC6IVƌHMY:QDM-2W./hd[1IUNcДqւ\xkrbL׽dgb^%$:Jh;Gx!쀴FU)22$Ux[._C c fE#JJs/b0ݯW(3$MCIXAIG!%L.=5/>/17uPt22N;Ypze%| G0O`D^VZk4_ӚVik[O?(<]p{r%>,df^vP@ g[2m_8F+#uQ>$i:~߽qkk[{说{l/&%\B+'o0>j|[SZ):3j/;^[lS -r}6Mq?OZVmU zz0Ҙr7fp N7͑I*,' H4z&4&5%o׎:_:"QD$'4ȏ{u9JP+SBMMZΓ mjj`lb mj.ˆ<:=? }b 2ow sL.9eRFc{0a˜&Kլ_^_A=V#e%| N6^׎٤n0ViI :4:ڇ,kγϟ"#,Z ~p' _N7v6 ؜3 sGP+ٮVH"]ŕyG?`S]ksYYtCЮk[Ku)B3 =WK5k[~ƾ7^t< DUGfq۵{ tuO.=qDZ}d(T]'O!OQ.xjZ_=мXHw̅NR:\"ˋKO\L;q04MﺓAm0I]+-{tG hStR#܊ teoVhkwaڻ+.Ov N:a-tЩc3 zds5f P}r(8 iٹTvAw]vg J6dg?B DkLM> m,U!j^&p<ߡR#;^aԈv_۞ !0CgXv=,**|,~@>\E%m zGh݈&HZ|2#Vl懑Epdlq{},7#cjp_A t~L/ &gP'Z4M~zy;(xD+# M"rVA@:}$R#UsupK%tjt2jTFO*ǍD+9fFXJs1C*BLȡC}D;"Lmڈ\kⴢ  ,mCf;aT~j&w^O:~ܻ@}+ k܌)N'_};Pq/W쨈x7r )ٶC9Ek+ˈg&~Yt = L4 ,D_sRd(n7Fa|>N:&lub,:=lMH`?|oґܬux %+V"Gv@t&9\Ar.dJP  "Pi`o%ȼ%]vƙrU\O X"&ڏˁ.dGGa?紺 .p}c7i҆ sv~q%wKC :<:lK&2\ 9V9M{;R-H4W~6k(J#txeK钒bKkm7!SRSc+dԲߒө_U'VS79 fF HF--d^$äIG!0Vp($1Kc,D 6g*@_Y#M̑w)%d5g_Hnzc)Ĥ_AEKs$붱(/pb{V %Oa^ԗɢ}h*n Z{l8)k$SrR}K(G/Mv z;SH-ƌ7I1J"0a9ZG[P2ymf#{3[#ȳ8}^̳f7$89*,Jah)._ %YYNGD;ب} р#AG/<ҡ-dQU rr{=B0p5jE8QMYT9+2"#=$ʒBdF;'+ :)32F<ۋ]ӲyՖ+[th|K&c4\եź>| aqx)CaU@}NW/aQI^0Kql8<욊t'bV\ھ@F& ׃E!toPpl ]R J8Q)2 ?s40oDSlqx>Q'PYԮ,w 6APx= lny 瘚_zH).h޾x/[#UB_Q޽>]p8[W 2~~p DW}gT|x iE5-p<=fb, f 1|~쑁A&80'sTN=rhP貔K ޥ !j{{(*^ܬ9a.ү Ȑ:iVq5u$_B}wGw(XuVi?x346H+Ґ-bt2_p[vN$0ϞBK0܍R+.h[+*Kfhmw^t^JHj|G2HQ8)ߐLA^-F_`c-z!v$Jq@![~ne(AzYtxuvK+l{xo0%t8 1~TAvkmI^Lj-JA{uv kY _aa!\.M`k뜹e8QtAoyy^'+H[/ʿ<+:~ML8#&*ШH2*P#̔gpG>ٮ$ H]i&O"I H%HEt(2rM)r ogGAu1^q%)gJ&}7FLIl )$ B|)ݴ?A ;4D.IIM_pbACZġHRC>@7LdTJj6v6+K Fx ;s2Hͬ#l{ ,k&9 IYNgA),d88XKʡS"&f\B5q=+& ό70xk#%Ud=2\jl"ad#,eUGc$v'i1>O_]<&I)^d'0g+8M;hP HQH7#-Ckra}Ip0en % Mߣt0M'MHHL1Jnw2 ɤ&)^FFst P:=Mr|8ϲ.C9sJ; 744LqvTC4j'\q;iRm%MG&Jk 2vT2K)=>~*D}Irlɦio*2xRr)̓!cGZxL]dqau-Y8957 ~]U()7䯰Mlp +C>*3 RPHyw5 X3)9>OG`atP >l[m$_y Zqj" tgrMˏmD }f8#wc,+fCcia3e)_yg۴!vKQ1 u/2xӲS $z7x8T^gO0_FARAr#]u0KH3OAlѕ,l [uP:86Ec1kky\?-rBƞNEfH7,4ؙD'춀IUR'sf68~ǥT>ipiV6#?G,7~2=)PĉA?3O HW-&;Mk\If#X{mzJx]S,/^_G*Ł=X%zbD $ | y='m!q2(]=A` 73Ybϒ_+*CPخY+m<~7f[6@0-qcI0qEH9|o/7h7l$R7wє?֮Gh~Ĥ䷱K(hC&[qh|"Y:\;Ez!} W100644 version.mkғJWyU/+ܥ4~xb̗!/ [}26 || 8XzfNL&xff)pxqI 3E  L , -L 7eU1YIpd'd,m 1\ P_```=X[v7%m(x340031Q,(*-)+.HMff?}v$_x[kBD Yxpl -qgjxKrBn{t79jT;JVM Ёf8@# x8S7G100644 iprlib.h&fF.ABYa$mdn&(<Hx;"-qg,\petEN"ҴԢhc邊ەT+3OOeNvuY&"rLv<++3IO'd y% Ub"Ŝ _gfn$*RӚV!58Ud]Lf`XBߗe:=Ks{U&ߘW2m_=\鿖3qM(dx@Ysz`)pJjY|^bn*"NN59E, ٶf~ôV=s Nl-rb_cx;} Ռ FF V&V&'024-~ Nwx[v L- L , -LML7d/y2O x340031Q,(*-)+.HMf03qJ{O hmQ.[$Zx[yBsrYɬSHxzuCP[CL^3c_]9%W91 =.xzu;7{qn/ʭX9Ҍ Nx?ԓ,P h6break;slkN$$lT)xzu;= /&++V_ LL2 22^W̰z uLX-/}Ζ$~ƆҤmWHٻ̦&X66sZxmU{LSW9ޖV(ꊔ07"@T.bj-EP- :7DC[2沩Mvܖlѐ=F]t[d٦ؽuMs{ss}?}}vOٌ_ۈjeb<̷iҴaŷ[犓T?πulK2HǪhRqIu` #Ty+`F1%Um"Zg`Y8B[AWE&b(LU6X CC(C T c9$˘1=+ csK7\nt{BbJ@Kq//Fm)c>PTH/kLTWeK)+ ;`f}1$wUKJnI I^tf ,³TD吆g9 ߭=la뛪-L;( !ALpJ: ndZ,i9nyuNqy dBj-8T#t:`ή{wH :Y>xx^=8T͐xb#cg `aRhXUQa7Jh+,)0kڼΆ5{6!8"=IYvFvv9cMNOZۋ?9!\S ,hO%XT+v@' $I,eZ{,W"OA5//tx;s Ռ& F &VFV'0zm=GhMLx[ve L- L ,, L,L-6d/y2ߪsҹ x!8t0VľV1>i2x; xE߫ݹ$&I$w­NOҤ; Hày38@ pGYGX]FqYNw3;-G]իwWCۢwn>plI5i,Xoj77(:˅gӣBwOǓ Kv\X\EIy&hk46[=BX6lbh3)SrϛW$3.<[x5IiH"Y FT@ҫ+\ҭ76Ej--fPP왶rxG\b{jF OHo޽]\m iziN}e2'sES<{n=sӮB\RbӂEgXdi^-GjHD"Ztӟaˍ5@rX;$5BQ:,nIt `[dKޙDKqw0$`43B(TtZK뮧i(V.1x+H)ߔ!/B$mG@J-%f7B"Bz}BGOL+$z)10REW1h`M0s(I͈s$IRNwj#UdI{⥝w& C:\ɱVض(lQhe HhUNd׉61jw fӼdmvn ևcH-= ;DSVh*cWcյNKcTtc$B pD<̖V(E \Ǭfa &KE)xu5z#Nj٤Ҿ!_IRi K0>F0_ =7lQ9 Ec.f5jUVH;z[{Ⱥڧ\3J^'uxOwU(yD+w(y;xVgtao'.(Ž_dcv5k#x*AZԜ Vy^0ǙhN\ )YcQˡ7Ur&0g`r^IgO1wFsAZ "PJDwza F5f,)ۈބxhV;Xff;zF S})xb(#B{Z"_xS.@V)1pnׁ CXqw4~qH_.BQf[:ڄnnQ[50p1(()MZv3/aVVncfXZoGK!3YU~jP"o|=ɰ>%V%̇r6 󝛙s-Cl_X-ĵbtl54_o yžG T>ʔZʫ&Eba17xq1VfXV(o_ Oٯf[i2K$I9^XE4aJ}A,z #,;W{ޟ17@lNUC yajOI-pdIwNfy9oS4ZE#x%\⺭0ZCxsc#^d;}OcLAM |)ɕ7p_VMp&$ <MB5,Yc^د4}=,P KpGj&!|Y`}Єjh .keս>TsVHcRw~ȑ|F:9'GYQ[T|_$~.ir|~$,3~wzd8|UX$ݩxi7 3\E2q+} ˧`}K|('@z0ԩ#07.Df4s~,|yfiXaop]*K*a-o9 G3FmHݬc0!)69EW $Nsuf[-z ͗IŤ"FFDttJcVx Zp1#ƌ0 M:CӣRpqH 爸X#[% c:& yH敳KuIؖzIޫ'ɲD4A#;g:%[N-,36aKt nnvo[}GL~?.t[!}k8u|gAmex;C5p4Z~]?&y2A7<^v9ciN3UOƓ' ]Tl~5Kd$/Qڎ jAXs鴭6 yqʯb)V= с2ˢIZbx!oCvd .&M{jp( ,(3*<*)X0cb4٥ aI-Lƶ0 x6 N8UK@ CX ܍EY(+ wA*pPP)`<*Q肔^l`^i!o]6Ϻ`Jo2".+uCb,NJ㷭`sXZM.뭢:cglhy|@WC{Zى/c/% Y<2gmJ'YA*Cn]D#KElEY`#i6SRŻI ~J-!(O1.˪%~*h}->$`|?̢I:BC_vkR,*mM&o\A"Cqd?q&7^ > '[u$Mev-GՓ(,5BzWkHoZ7E`ȘQiwճvUmwWz/rmlSm'#3c#D#1<"weI54h_#H'R"$km}Irk=/{H" xX|:s ϜaBsAΐlz.IdftT~j00.eB߇7eb)zd=$:odՑt[wMK? I- ddsU] JkkTX#- ;nc(3#Y+iۑ#8aAJI\xdr[qtIrVd\#ݲEfD[ KlI:LjOɇOCDcE ? 7K%Kil}|MՅ$w-- H/:PUl IU]$Lz8I F|bSƭYT':9BvolLwhF<_ԉϿCZfJbia阰q'z3畯f ?^|=C~WةfqFD?rH~o{?s#˥w5xoCv|7GvΥV'ǒUvyeP~BFㆿR܁ګ$=Nf9$W^%Vu MVmRI;[{oq@@Gcƕ6 vsXZ?{xQPMCEg&s]l<.&u2]-N`^T!&[atvBFUxV,t=t;+a6NJC5cN@ߎpG4Mdtd}"._m5MNJ̬jzVyºf,,j$) |ǀӬ!KqQ4sncg154;oXeUĆLBnВ\=Sf\i`O6q\Zsf}ê؅4*lF[7Τӡ\W,3*[f}}?df3a8Jyaa|D~z.)Q jޣ[ '+<=F4^w0l3b#rD/XLdC'ifhOΥIVFuL.jjEM3T3Vs:QJX\BUC-\ǵ4?* U\:mN*M^щ V- oZܠ( KSh:C /14WyDk8&M%ڴApE5R fmm{U@) njl*PmpcwM//}rJW(oaDA-o{9.^|G@*PY`.dRS1;woEnuW6SO7]/P_f=mwSTsVz.v0/-Vnѫt͢[70=eZڝ6sCA]!orӔ N0vXbUe"P=p(|(J'E_8TT}T%\44Bur8ÿtܷȑ| m 1x7oC_Vy|Abzj|FjbJjTԺ^j{*ނ̼t M: :-麷 _7!#B5)}99R\0UH)NULSOԵKI-Ή+N.ΌSK5j:_ekøSrB;xZy|SUI%(k.,BBY*!ymEApό エA: (.,Ha\Xw{/iRJoG޽{Y{kcs #J>amMVXXh-V|oAkw8}6R:>)񿔛.K+ff!-v_S`K^,7STKoa}YwH5@muCinŠǤe@=Μ}/m^Wra,}gS$0;fϞM?.=!_#l{}K8H<-ӤuCGd4~x⯩K 6l$RW2qV\j<7pȤ >C|3I[Y!XK>˼fqdKȑefݲ̠w?gG+BQa0`m')\'u51faf~:U: =ۄ`)y b S]BAv6Anivno7g3بJV/64ҁ'K)Rw5 Y\:y*z[ $ 0)2d5;傥Φֳtw_!WF>j)K^/f7JBqwI'ɧM''?&c\`1㹏I5ydSb ݄w`&l_ߟMlLD*ʥS6=?th\nU{RZFz?.8yOq{My*u(z[}90VȌP(q'{sBN5J[HVL;>f'$$4/˸@󩴽#Lq$3y{kDi7uoйB+Gq-?|1Zq N;܇ ?cu-VguIjwy]: LmDA#2%ܼrp(w&C9~2|K<ŕ@LA̓!Ǘ<"P@tD9#9A[Q6؋Ă..<;# + ;" PqBG. Ex2Ivy1r62;{cU};qũ^EFg]5@j#S)Y81ls;dy'([HHu9,6>",> i{zC8s:v4Nt0?cDBS .jK57~Yb@X^ M[AT,%t7Q.\j>d c4&ZHXkeTԒhW!)xa$;5nncё]U H5#Rw E8N2䉳b8űP'Zrs AxHF H3c6i"qƓ,&Pڠ34HY|iBA/TΗ61K6D_2hd,מ* ˝(Z-r5 HfU+H*=r:I1 |矂,FX ׇiYk 40g ܈[ /Zh`OGvBVxVJ0iǜ[ !R5M D>3%ީy% 7rO 3bJ5A :FrͰ쇠Te7?S\&(L vpΥcpI1*3]|y9]>³1. `|gs}_ -bxn'ʘY^-ͤ1~8 qHbn6 +eIO#<3,h6XF#< A3T ԫ1O5 OekBYzFT?Hxr'$ѝ`Jal⇣ŝp +. 2㽯C%~d6jϥ*,+ѫ+y1Ih n3H ^.)g. cRf*id} > %LXWh*oYG0/=&DcNbLZ>2S yU{P>oJ?t|b̨]m$!{3_^os9}g5P~cU+P޾xdy$k3o|?+e UÐ@   8 #9bXY~0D1{{:1|y׉ҪQ̳XE&ֲBQTriAN?E<ߗ/7i:h;Ykf 4ܩE(ff E٧^Okh= c7@+L5~#JsEO} #pIIDD(vGh}0pr5~\_VZ:G=;}>@E(R KqN}) UA.3_;Z1OF/ens~iCڬNpX!e&8Ά(fYσ#/ iv1R /w.W1s j1m+(^} ~Z\&"^bI7>.ƌ`t` 蚶 \4DIB=b| 7?=qGçt(gu@ڲ Ӳ/K#%5B@Q8UA9L:5^*'C* H^~~d:!f5Y->jLA\DrOdbj&٢^>a_RI^ٛMWɸ%ǧQ7%45<55`V:HvK尋*78Ken| X6BuT>(xY<0,Sgt6ؿh9pk Pv&SG RITTOr`N'c\ pb7%87IE6,u[yLŒW^=Y$3i~ٳ<6#$zb%_JşD/B2Aεxџm#Q SXBAcbf+&r&1,[H$Albh$y;"Q>% icm,2cnKWX3~q!Q\I?a D;n!p 4L`u/'WM[Iϯ=iSh†| IEE?CC?Ɖq7$]P3H9I,m;):h4޺;K0F< d񀥣y^Y" n.;Z|DULwq/Hns('fL2#xcM!/;>#d&2MXL J>΁„bfCnN{YR+K}mnCpYGy/UAKt #y)2w|D|'"߾"2v;&C)1Rw]xk?'_+6EK ZDQ:\^1}Dǿ֓}'j"3>>蠁kZ: ri{^wt,;j7dE1$G&i;K!Ia a/p B__FF\I"b%roFǠ6lcdbi?O:aw$ lB6%D}MqB96YU,-T8!Kx#d 7VV52qS&LeN贶Jݚ{}6OVWVc{pԪu.㉟d~-FǫYb(5#& L$N`[7F]Å#!.StJVȐp=)Npkn,>i֨&? B2Q)R[ZFK\ӷg:W'CCyfN}||a꽗jn&NbsnJʭC 5ùZ*~Z#<zw=pP OLlD.O+ x/f)uR'Gj`HJj:XUBu2^c 8"MV(!x*6_96EX4{XgGgpW6G8誙™TpUMoFWh6I_:B̘bs6 \e9Ia̩~09*++/Y1tfT*8pϤX$.FW.q[BzjY2H%YhpS ǃsH~T$3s7 ۢRtb5 +V# ł`1F[ AB)H.?,ޅ OSȅmg Ax';qjȄnR6!'m -Mj+_.H:|qF#^1 ,gWc/H_?ek:gaLn#I&kEڂb[}B.?r{!?5 ilrdP+z\R>V/yTTFK Xm.gR}ʉ6x!.bh=qI4JXpN h43XO(Gb݅Ȕ;uJ@,Sj`c LSOD$kWJ kȲE TY 5*Iui1,fJLlV5<1XZ5ѧSzZ1a Aܔk8RgTA59-.'imv*}V)Yxy_4!_8M8|@]A^Uvۀn4ABu]tл5:B6Cp׌EqZP"sHB.bk+RC\qyvz+/]sZ$L .fۼDJgE˄v8 |\#Y[Z\6Smqj47*Q2mcVuZKk'/2R{"Gr!-*n kV/ eCmnK0pzגtƖ )G!-_27G)J*8T,+с]+ڮ.9JL' {IۗbpªAQQNLzH H:*.v!{*QfU> pM0/.,?O -xZƯ6+[`h&;Utȱ:_=8ofT T100644 version.mk`n4Ԁ$(f~k12'x{6-LW \afTk~ϸ]=d x340031Q,(*-)+.HMf=#_Nǽ;qXf]k:9x[ssByAKxzu#dGFY."C͙Q+xZ栫-~E` ol3JP@Uu:c >iI:^'ԚZZ100644 version.mkxW0'XzܞUC7( x5ֶN՗j{L `3իyx340031Q,(*-)+.HMfR=@qճތi72ncx[}By8?gxzu#dGFY~Cs#yI {xRaxl]>|Q$Gr"TKٙJ|}$vdbz16&S'*2x #/!>,١f/ +ZZag 30 | EXIT_FLAG?'̑gmBtv3kxK{3 2005/09/28 04:28:58JK<4~Q qq + 1)bx340031Q,(*-)+.HMfr"_Z'Nߔm>5vFx[~iBsrYɬ}9¥Q^x dS}\Bؐ Dxzu;ȧGӶޙ{4Dol-'K3,q)/lڭ7YM{(dd&e0LIŽ83Hiu\񏿝ɺ:ѓVܜ7,83?O/7C쌧 qfwJ 7h X@!@x/LJLH,RHMLI)N-UPP1rQ`%E% %E9y=Yr'F[n=>dY:o.==tfA:ӟ3gNegNI-lo `cřWSK2R: y@w22 .n8=Y'LS E99@BqfUj~BM- 2ԴiO_UȬ+$PX^;=1v"FvrF~:e20=_ v0̬ו2+cJLg.cVf#PG7xp{2 2005/09/26 20:19:36JKWSOF<4~QtR(SSqq + 1)T(SE )~x[viE L L ,̀L+CK+c+x&;m75|tMϧ-x340031Q,(*-)+.HMfhv^;WҖ- ?VC}x[~iBsJjɬ}9¥QlQTxzuC,PpjAIjnRjJ$F 2x9͖ฟ 7z@}CUBB&A\x?1 2005/07/29 19:41:59EGH$ҷY,q + 1)b=[x340031Q,(*-)+.HMfH*ge~K*{]x[mBypAjxZb:Nl4B*ޱ:.%~b:4E<100644 version.mk1nQTڮJV \) x}y|TE:Kg:rНtV@,$ A  M@N'vw3*Kg .yFm#".3#22|"8q8.ȯn/N3rnN}#`67-6405_gXдQTQ"&尣,vHo.. yM)K5f]5&ziS+ܫ`~8LStצvɡ=.U+2]\ PadphX8wAavkJoɇI2#hhɃ9*/A洯)? ۟Uvr"}u7Bܛ>3yv=9HS`RRv-:>T-xzt57wIFxqE+7+]{('uXG:q=pFLЍGBi֑sG>7{^xM'jyv]~q2NՄk [n ےZ6 WTm:歈BN2Ʒi7jh1:M3jRhxh/ i:Is|)bha'h#LYD^&Gq] Txk:NY2G75X"+(ZhBIEU++T0>pIJ0eeQ\t2wFA$d e,MVy1n֘0ہ\XaHWl QVpɲ)XB#!oTn@U%΄GCLE&T^LQL?B} M5(g(X8 Qt%ۿBaQJq.VP.8yG5-ӏp6}l ~t E'*W$xQI* ˧bxF/^ukYZSH+H֢bQT \DjcIU>rȰ&[ ?EapԄt Ϟd;i]`bF;rIXL/*xDW]wvDJSWᢳ \"j#?uMNSξ^)@xe[yG%BC,(h~dFE+yt Q+}8c'q1=AZL8N?BTbK(: p^h9ؕJJDfPxm^y 2y>gI&tx;yms2l؃XH5(Z]=}/ "~k?J͔y"SφyfxzFSQ6-,t]O|+R ^%*qJ{,.3dt8<+v^OR|2~ Xx4OE.x(&pa ?)W໋q"=׼(e]s:2}R"2z <"[ilER2PmNΔ2&*ʃ)1KD/cZ;U»)J3c'v/mCܷz$[hyR..V@:r<ED@GKvP bgx g ,A陼vfڻzl|]%#BzK`AEϹWy3ÎQ&Pn syL9؈6{Pa"gClA)nb%wBC3-dcPZ'whQ{Y!z0,n6Z]mZ1S/KQZg Q\H%:)ك| 娼Dԑ@{"R!=a(]{NA@*8e@TC}TtA%Z)վim@ߜ$:}dz_MZUZJel9P{)Ǡ اb8;<? 75($.O@+#oB ݷDP3Qث0o5{lOrpETr(6#xHgLxt!>sĿ ӂEhY<ܔQ t4@5 H"B+FJw (#5Vd{m&0>3!bk؊bxg6q6,CIJ!t1;y4)d aCA X$ЗZi3vK*E$UzǑ^c;ǠQr t%Ly\1@oIZs1 & /G Brs~1= }tcV2m*g$+v9PŔ;.ɄC1h_#6.L p8$N3Q=u1{ڀ2x pb y0ȐJ|;ݎf (tWH<0uNqߴamtz2Wضڶ.VY{zԯP}'*G4lyrVef0KP<ҁr [Lv*܁Ɨx%-ʶ=<eOA֠E;čHJQ)>TY3C4XV$[X:-6KgOcuZfY'y=䘞O knPRVNRk[.l24pHESޔB,"S#Cg_2v~~Ӗb h^YWTDD@h 頮K4eND}Cf]*$;fdƥ<=$汜fڗe+K(+ z~ HO7]sװ9m% A) >=נQa-@Qʌuh; qL=`V6 he4ZL7CBZQ3D{7}AnCf4m&0;6 :@-HX E[*xx2\:UE&O;jqʝ/6ۙ^%qأF<݋"_UB۬]3'n')0~< ,x+^GKqbȥ6a }hPZ^@_5v"9tU{!^?R '{Wz!mџqKڇn241Z >ڋDq.B"TÞp/t9N<&&?B2r?wypz?j<7$a]7t>HГ7o~D@7ufubxP.}})- ?Eaxy!sR|?ڀ&<49-3|MY"#aԃݟD! 珣_ } 0\v3ݩA4~G9Pqǘs>Cpi64PKC8npnv&Bs( FxZpv7_I.6ⲱ*/C=vmN /u b,n,aE oIA%M6H9IU`>־ΤKQx_z "I3˼a.)^.=gd>xzxCOm)L(]pAbخ5Xy +J ۺ QhUƁu)҂[d &A\L6@r 1gɚξIw3TJ#7L^B?FY%)X)Y-+V:%Sb(%֣6Kq^ RtNit`|Nݼ`aK#lT^dpqpӹNˋ$!G87)іr]ʥK7r/lD+VsAfZJs*\U(Mp,&qXஇi|EkFag nz-NHUmNs>bFA}/+L}wX fP{ɬ65(?tl쨂 ^kT/+za7Z 팔dqx)LqrpZ=kl>ZbaoQnV&+Xxcy8I2WQ< 9%AN}"qT?&ٍYtd7SܔFӳf{ U֐Lk m)kzZOtѾIߡߡKی6I%|%鉪V/i))bYw .ZIjߎxTc#G4Ҙ"[n$y\@}vYLj |8=xE'Ӄz1a+̫v#O`)2]RhgJDPٕ@9?tUddxd;Dsh((`"@9Xjy}`I3G:Jo|/xϡ֝Da8) Z}>q8I-A2:"QT .9 1%Kf 慪ɂlUew5`> xL6 gxl* -b-\Fj Nك?*G: ]B#-x;+P-?IVﭛ (qR+G>w HB=C&*~%lf$[P cӯU尿/MϪ5wk"oV2N7{dHV˖[гy1sO%(.t$7I D3Oq"OD~rߔp~qCHл~mư&[<`E -t?&RYlˬ7L*1{Lp: KD%sӦJUߠ48Scͻt{QΞs8)FU87BOP ؋p3Q*3~O5<4Ψ̉u3s7PAz؎َzfw@Q\RX;w86JbS1;P,*;tņ] 8u7sV*VY5ނ o tE?y(=?ENȩdޞm \a7 q䗥ĥLD2|'| [RUz81gк#am%^6‘Cx;aN!*4mV@3D .ܛ'# pAR7M!2= 8P6H"饭e:3Ãǹxa.a)\dX MFBL1U{kNðL:2n;F}^52Qy'H,n7LCo XMߤO%2e1{eVjy>q!T#ZpGNq99 lu8Y= 'CN!Zýp(LWA4Zj< >Ƶ078zjղmή|ðČZ\ jqf4mڀ.XIccΩ HM 8Ɯw-.c) Uy1H='MM"VbH"\FY֋LL Sp{n>9d8,_#p4kokBi)K Wa3$>AݝO,5eEl 8N ,V`m2{N߾6;2׋eY7y6<aɌ?{#Z åA,/XZG27b%ކ`_p*Tce}x{|;ii0S$↜K:|:<&1A]8?A$`X _lŏk!dOnIߓ$ 6_ =v Åx7~`㷺78t؁ທx ^~~0p,};oTu8G %HYu+"ĜS)ՈOu1zX5λT'D~'eړXMY(7#᯽W=gx0"ЬA<} GG8qQ16bk=ܫ[ヅ=TqڣwZbsF^U_ᖱiaX-)&\^8)7rVr"sSEc/'7=T|At'qQDXUfG<4_;ʥ汅G`c N?bj#pt;nIgF=$wvGY(:A XEjYDSx)x? TXax lwn(x'˙O' N+oj׍8tWWt˛v&Ƴ1&#@x缍=Y} V{DXrs=g`ڴg2c>ķc?\'γOI}8|#Nt>qo>?+q!wz_~"}O+>ßO M$ NRv}$V72XGJ5Re*8{NSNҮnc2Ty׹v8"`"Ѷ8*D$lHO )@;b}H>'~O)>xf6I3 %^Tuެ sYWHh 8Gu6ca~2qHX;ވ 94Oo\EL{ -GP Qr?i_x&"ߪCߍFx>e7[(_f@61=RRl҉ /󳁧\$'iz3E`2W3z߅kyhAt{lwȖc4DB}!$?DUzM:<B^ʓ(!n%?TE7xEIM/ӕV뙳]IFmosc[`"<{_HqD8|E*E$ %Ѥ>^/;³1D<9/XA?1l:,G1 GBO&x( dfpM$}|hdK8*ț!7]C*"ԐT-=LN*܁SHfgOMBϽ1D7ΧtF51&2%84ɻUdZahX6]Y3ȏ9e$A-g+f~|@_N&+ D`GaI<.24"e4J~)3g/IC^Z9i &L%'z [ 5"`@Ǥ„n QvhgOp_7r73B3mF*Ll!EBiR [LtxJr TPrC*pqx(ia'ByP?v g$6rCbwMfG??tC$ I,/?n]*7Zoyܧ4җKs5XDSz(_Pȡ850$@uz')'Bw N"ŋ/WT8٤uuWle4a[.\dS`%t^< %źQ\ުS^\X%k/EcCs#G}$Ξ! ءAZ.xU]A}Hm5i .)[= B e(h=~MtR} DE0( 8 zӨP9?TleP7, nɁquh2qߡ8׳eBΌ[k^Vp~O(rP4.$Ur UGɿXD~M'78!-D!3}>NJV G]l7XR=Hw2Ywg%#Ea`;ג y?J]>ћגqa]^K2|Hʁ3\D*=t nVFvڀ>@_'l̸̙%$ƽ%L97˜uy1wI2n$2}˕Gq_3S*?Ϳ To- H]LO(&a5D/! H vW`*(fRNVR.h%|[̝̝bb_r*?`le<6V[us{;wFXCKO[Jg3ym_.iy\^$HvY0?7E:;D4W؍&QS˘~XƈjOB]|Yl`d'QQ-=IvHH9eak`25DL_Zx2QK2\F&,qɦ{#3zn%*u$ )hB _lw Kx+m}4=HK{H{^rz>',%+``7I-WH]6^gFI\fЃre`nJP^2ձ92EO$dKSlnGIޒU׉jajZg+2xCv*=N/yw(F\ /Ө ?˱ZUÁb<>TU$ETzNqד3U%Ɋ Rr)&G]OlZ3k`w5r09PʅE[4#޹o^mq{lmZS=i.Vg] Nf~:5yBU/KGŪdYQ5VQ>Js1;jȋwlxG~d]l^r H[8mǛT]?x340031Q,(*-)+.HMfHra{ԬiqxvuBEsB;x;zu=#d{FYvҜJ#Ɍq,Bxʺuח{^='q(WEDwr?u)*{RD!(9?/-3]/ᛂXu%_Yݑ9Ua(`8r?c{]^%GR73j$QYDd\)}+NPȻiK,ow]d}}Lo^WjLtFLPX_^/.>W =ى)jE&_ӿRwPeEřyz od\}|쪈#NO{z.BSxmS[LgΙ3Vf-" :ZivvͲm(L]/Xw찃@J4">_xi&>(՚zV)iL1}0:.|ʹ#$ɰVc-@^4mQFY$s4.,FYiz?TuY ep'N-xx`/nC9w!@@yh `>3j 8A#CcP=OQUyjb B|3{oL%0 y ~"Gy~Ay<C~>aVQ|’PK(S+$ga)إY8`QWú4i:@)%V.Hp4ՌqwPb=C[\NJ]d݊U"ԓ%eRݰ4; $)IzvB8boD$R=I$ ` 6MXESӤ!&GLeb4|} UmxF|SU Rag-KU칎1H%QL:C7,ծb͜s4?A[mpP+y:,lյ3)Ϡ5brKeiiܖNdjOE.WT .&}fVq=?>OСtśJ t~'Y^ecgܥS@q=t}J/\"giNp}NiK!tMuU(*%iFOU`;N̥훱C@m=IN ڴ~IË,C~̙? :f}[C9TAx340031Q,(*-)+.HMfx6Ky#.m}峹rÕDI$lx;j/9,U +x;zu-g@P|cmJjjUS`d sxʺusөoLb?O<[4zEO1c雮㶡BfAQNf^Æ97ve/?Y-ZI ]"+4K_unuY |T-i9mxoY6EM[׵[RQkI$I3^Y1νijb%&iiҐ\kybKJjo2)F'{*>nHf3^%&`5}սV(DZ=6&Οnjܙ ht 'fρDE/wZ;V[wk^ on\Xgj3wۜ6նD*/ Yd2I:IuNgoH6nQxg5&*t$-E2E*R*,$z+&Tٹ~ >:wiR"|"x1W/#ȟI o@EB`rkbˋq7CrÀ(6CBX@!\yߖlc05[ŕ+ș1DsS'q$Aڧ4xuAN)T) *[ coKKKZAr2RjDHw˜exc:f'BM"|; o{%L\/?Nl&v3Tm~  lUҹmSثrjb/Du #axqioA8A!i8\Hoaw&a[y&C+}lɚTvzPL6X4AH?b¹ y1UCI 1E"vnC=Briʷ-֎_>z?F@-fH)go0Dp6CEPj>7TC6B3Y wmRLC`ҧH#P&J%j]KGkz%J0 A\w7NW-HRR%§tX..6{AQP \^rS'J pɷtLU?M**K0 185ȓ+oLDd{}16Nبx{r-'Fkt8 xcة fi{mL i6LY9 S}P#{:,XJՌ$D}V7Y]kh6Wpr\y|fϝe@tДgJ<I^vn>q %3_29wUWiw}g%6z\**K/T7}U,ɏ}56?*]>)XDziކ <jb*x!YXr&څe wΏ SLy"É]$V~p&ʇԯ2њ P3z0|1AkYm3| ); a,e4ayˇpYPE afz!Ud\)qߗ`H ArM)cB0ɧʇYdzhl@DXL" ׶Ѭ 0 EnH+d<5jbCL9Ve,.""I/X ! W{2_~ qRp)}s/5•P<+9nh|)K6OyLyfᅘ7s"pLQ4qx?v}u&5?/ Q)J=k-Y:$?}$+x;s+"I+kqn ;q'ۈ;[=VZU^u{o0 /5pfȥت`4lIƖ|XjRSLŚ rW)TϭF~˜ᇀ#82n!즁r̾yUH0  3|௞)xf1dcqF8I$ TA+3­K#?+gpfYZ?nluuv>c˗H']Ll:S,{$GWQ#a\ Y1Zyb)sxh2pϢIxj)$ì`av%wĘa^&x(p$EY ,wvq"y .h2=t tV*Ow!ms!M8BAzPM襼};40;Ϟ\=_h# $MSo>F[O@^ Jѓ {lb3WNߔʹs3 Uy^@6Is $k.x} [ ħthBl>CϬë'HEoRsPȵ&G6Ǚ+21 >*$,7KVB p?BzMzS#r\Fs8``aIGaR_gI( +pW%y "}M&9WN9ϳ8(%58e^b:L'-Mmh~z9k<Yq͒ϭ\pf0T;ܮNiu ]n_5^GM/]*Esg.Z2E@bg?]R YBP% o. 9q<^hi`QCwpiACQR˛Si\*&wߒR~T7# qʶ`rE,EC?ArBm2t1,05PdiH凳> Ÿa^2:D!9DLTNP`zjXnM`Qog!]p_ޅ[c=H5J6GM !u0&"WS&Mgݪf C>,Rvðpc_M+{!g3v$]Ǣ p33GIj>Q!ʏ`ް!G S? V: ǷA*pbH%>:>dZ/tT-'kbԲȢF0Ҙ] nMJ!zTϹ@–1O,p>@U ԈW!BlDo#Ď*uȂvVO0_pe#oP.VaɋZT= .n*Xr&~1Pn7O V/_0_bm_q_),,Ƣ>Ur7MxsF'?OS^>@ 70\ c&;v]Bm| , _ T~#/}"*hr럄/HO uAhvcDy[HIfjl,vfawy3㏋7Y8)$_Wʖ\%,<6wi:W RV[V[=n^F$rQl FH N\vjo`%RKH*>?X ёzd/|d)cnHˈ0t;=J_B2x%W_M$u8ź|9@6d9WHeLmd%Aab04I ݀1o,6B ${Kk$+MKYqD̅OPRA274XEԽ]"_ &s{H$!E(=DpoIF㟺u#zSG0sBBs:>d]53 ~Q fN@.>~?F#=(/iBf#MH bUc5BfaI^;}J|Ik6?Zˎx9FM&=zH)7dրcb3F̌7%#͔\GTk_ 1LGWkxM ɬ5_$in c|o.rߥ܎"j쁇ɯt|#Vے%' $YmOO-AOOO'sT92]iLy/4O)h=@#U/:oe!c'_& z5 y| E:BwȢ> 7[<,<53qg"jwu{8tPSlOB~zӷN_ȩ`u41đbuS|R}wAx`Ș?ߪe! ITW'c8sOBIȡ=~͑ҿH%=aP@s'Caz8"\| rǛpΓd;)'O; ɸ) I$r8q $]E/#Fbno ؜V]'f֐&H\A'(/go +Ͼ Z"SzOȊ[jwH|JG8l.Fn?ecT|6p* `0AMB$C.b>6`!#BWc KБЧEɏm.˩ "RݳAP"U%m;P45"RKrMQi\Vr/<uLr6kϕR'TНD=W4W bY*|LAF^ & Vtn @ z^AqL53i8fR(Wwn&}_XWzuWJO4x ]6O88j<5fgP!HsbBgWУWr&WWEh q}.Oİ *f~s6-Pٴ M]s?OoþIt,Jae}L0NɝLrTL\,Xz%Ňҙ*%xvu bNdyzr-WhEVV[HT+1^:mY#4c'}qM?hy-D?%@yRz> 4:ZP p;YiS{~*]{"l, `MӘ&{w7Զ,Ƣ`;q5L#FKT"3,vaxslӸ_Ij4`BW ]#nU4W m HŐ&êVVG3.kjiZ;/:l21xt<9bnz-Vs|Vԑ7ūe<1^N/SevRI40ӱc$V/Q4sC}Ex zMF=N+-aw_[j}<K k ||M3ﲵH.M -$ƬE;m`*͈ -4o Ldn$Omeook*ԫ1_c468 B}2s=Lo^evǜATJ_/:D]Ӊ"Uyn}f,T1Պ )pt}.m!%d 8 ܳ2vOwۈ~C;oa16wЬ c/Tm=gQ媧۲f3ҴyΧΦmﴸT:|2pn V y0ճ GO"$x_D $"^/>0)zf6 MPO2Fs4x[Z-Uh3::F;x(%E [sq&($BC0e4]UrAQf^IH3PBIX_XIG!'?=(?$>%h:.tB# u4SR@2?%֐(ީKsaV*ov$mbE^H\q$SylQ0}>X_=ՃM"h!qtI? 6NJD|?(Smғ \弘܎fc^)6Bx{NVFKRm54ۻ:ik?8L 9_֖WgK Uǒ%DJ>'H+#niJmky-XY$^h_Շ>kxs svج'ڶU|A?LTҊVK.-ns&Ċıi)a*p8V!✣IX/N-L,jsb3]:Ɗ~~Bc׭.@+4ӧ0EW|#k bca lvK䀑zz$]Fxl]RN䴷K%^/!vM2hnkϤ1A躻DPwx;IdQKFۻ%XiIAzRgz>(pW6ӽQx\u /,KŒns7KTX.oY3ĪbhQn (JBD,H~. i}XLcP_y >$Agq%;l2<nOXﱱK-^Kqt=N.ixixHhtzS*ma*1Sв 2o4yHVS٥z+Y}>z]vm]*u9|VhoqK&wO払1+(;Blg$woۋ+1"cuz9`&~ )k3ڟrw6粠C9ZFs :gx:"([d %H#||\ [ 0Z<Jk 2slmV4s}nghi4ZzZ ytw_C%%@+A"ұ6+>J[l-L@ʼ[Rqp$kІ{v{S')VI5VaK>dsMV"b҆SƄH\xyLQlpZ) ^rH*d rFwg9JWͮ\:$. ^;GlY-==eq;iCgg-p$NTZ+PN\%AX6QH{~b/ԂmcZxB'PÐJ  4O<F*I@.bsw0Ar`  KիEmy.3x l78I6ntn &raT9tu`+輙Е!XLqmO(YxTm+$GjQ"mߢiC#x+6qgֽh`+$*n$1V̜]UTH*_B:G{CPFbS*5ج͝Vcu4Ռ Ø]=(w WM zuidgXvX[%vknڢdߑ zRO@zƲ cmf U/ zzAi\]j(fBb{S?!r̄EcOCu e f2?t2 @ӆ4TsubdLZQH| eWhe$K^8&u*i%I 5T&alI:L Ī2JE弰tVț*Uԑ /KyimiyUc`% oh1RvSM &5΃7o866ݩSڛZO7ѠF \Jڒ45yRR/dUTr)~ix m7cqSA4?\QPouXM3cWi/f6g3eͭ7D.^ ZNjAy",DmǬ 5R`I`܋崠ύM*Bс @uLAq&6I~r9jPv‰jQbݍwV"FQ=| V/rj4`4SV܋j: a"|tQ3@^Vj_/& 3ϓ&ORX}G T}:SnAkPoĨMj܇ppzҤG)(ـk3i qF6 |X'0> ˢy䞢Nn3vmhn+~wx<4y.G>h?ФmK ͅw0ͮh;i8.1PDT؅Eun2pm\}ͣsKZNA7F-L2n\m/`F~%|+xR_$nPknGzs7eժF'Fc^.g07.Hfl'JڋVG38!vki QઔdΗ_atrIt=BwR>|Ɵ0H.KyCS mcmm97?1i<׎:d0)-4 bWc}ї2&k].ʒ({c}iEtëBޏ yti?&53(Q.1Q37*kvg3~vER\'Qm/i%5x կt5\dȤR;7pvn47hA4p\=,HG16J )SA|bgx$k%}5s\Eg?G }pTVi`aŊ4z tċJ(3h@QFduE^ˍ}2Ӕ^<u~0$Iy.ٳ}nSeSO:vӵcYRTM)ʫ(!A`hdKgM}Q03]JΠ &HۧK]SK NK|Q-vh r?Yɸߚ fc FQxW{lSu6 q^ v I8 pgĹ6!iPnP6[71FiulZiC´2ؖT⏵Zڴk$$nGw=դѳc` NbZ-"vpYmd 嗙m/,\X rE߀'Rr1c):bϓ4:U]b)U()x+m+SؙZ$7zڜ\Tp)f20{ )CL ߟIPھyJoǧjc=]ّtx;᷹^ e8M9kHuPXM\|EbNHs8sj&q>چ$0 g&pS'5 .4A,HgLƑSC"5hE:UDF. wy l "K[l#FI?MZ>(|H/Tc2+`>뜰?jSۭB&'sAxS"'ya*""y\MHVd &Z@11 h&# Gfy[M15qQ[ڌB#* Ng +m S#fF|f^]JLx<_1E-yK+Lf 5!-4N:wX\?Ki#VnK/h`gA.kιPbC`2!PݐŁNaZKdF[qm>oz(=FJa{T^(3Vzyٓh9_t=<6G]㽕[6vC,TRڼNշjs^[q{Nb4/ne#b8pٖKoJTXK ^\Ot*ŇÙɤ}xnB|!V={HRqrSU9`blU{݀oh䱝T>ԕ)ޙ"Wfн{O'א=n߿3`VXEgeẅxbys:.lעyf] Tx2^Lߞ5gT!ؖuĤR@Y>r{=V,8Kgl82k\N9aS͌N5@X)$VB44d]qOV}dX8Y<|rL/Pt\L#{{'T3^OeYyxwщL1r\OlN׽b|6s'퓩A^~&f1?X~hNKfNu`n̾;8#}1$Z(/C}05qlUJk6 Qy"Op#R>#X=bH'Gps F+?koȠ\,%a QÁ^AAZ-n1Q\J"]WTͦ'jp 2=A)?!ã Cƪ1Տ3nڍ|\Qʻ>aͦİc,3NDŽEx340031Q,(*-)+.HMfb9~ { Zym &5x[jB0ưyx\G0x;zu#d{FY6ҼTp"xZj<' !" :Yɪ|{]ìf100644 version.mk3rQx22Έ,x340031Q,(*-)+.HMf_9uB}4ohxh5jBE#$7x;zu#d{FY6JCa]xQsRcw|~-::kB:FzyfYAU"Yߘv 9 >&h!Xx;u-=q~ڼCv";w nx 2NTlsWFx340031Q,(*-)+.HMf1CR7J: ]}*T xhrBE#L6/e;#\[xʺuC54YBq2ۮ<Lji (x340031Q,(*-)+.HMf ޵{evo^hSoC)qlxibf_5"'G |xʺu7UY-jf:ah``fbYP KeuwI75>x5Ukֿ<1"]U2\Vr*9.ss22ByHߟ -B&+1EZ-}.UV_~pd=e[A5gf3O\âUmՍ.Z6Qx;uy-=qƳl6/ݼCv"; x{dkQTTX\ls/90Bs ~x *F13K#S} 2P042221o}#B=s12mtx{vi1 SEM LB|J) Zv)e )  We&[+hko|=ix340031Q,(*-)+.HMfБ<~3LyW`.-f/xi9trr]C#̤\\G'+O:y:3d]V5҂ĒT.hVD<&xʺuח{^='q(WEDwo6?[VͦuD!(9?/-3]/A(I-Y?}?,,0e0d;qXL{JcT(BYJȨO"2QǾU'(]4F3fTjbmW<70FYTfd[!<} ^F5'ucs2Vvո'}9y[E=KM&;1EZXh0?j{ay{g\ZT#[*/xrOrcsd )3I<skPvX)}:*76>Rn}e100644 version.mkd5 Ũǥ89xQѭMR`R`?W _a2=a7b}% 7Cct1"V6xXy|TŲUM Lvrd&dcI!@ H'3'd`d JuUQ#Q\\.**ʮ*|*rsNuwuꪯـo>w;. ExȒ5{MU6={rdUkJ}Ō Zբ!VXbSʒ#X+iX .UY6<-,q=:6tLo0{5!nKȥ'!>[7M9غ)f>6jWR90{-F/c][<:yԲ&FNSAEQ-c߰qCXX6UOӦvVAT[_.ֳֶL6wM%XL=ѕQf;;zEnʶLb,\Kde:0B=mkml;+"~X8`OO:tV!_>su8za]nﭝ%4zF'b+amiГ\>I -ͧJfo{5g ztU[{\F>Y48m6#ܹ.+eقMt7^þfk:2pBA]}#˘9Բ=3>U!Qv6װGfM2)s2dJM >Z< KnXla&& 镂[4;nEâMU| |:;JKg1™, X'i5q;/EW`U[\|#{؆jXcb×|:PG$ѝ` 4.3_lweuXOt;n]gF|ct.꽄 |Zzm/D+f<ݢP"7ָc2wM}`i-Y=&`ut9Sم"v/GgdVyRݗl/-W{X+j3;z%Gvnwv쁷~}"dޝ_i|9W[#v(칃d&Nd-ef74Sq_}ύB_7ARU,(ŹjQ)2Lm{1Me/1_`<[{Q޸8R.k4!)L)ԴE0n0h*F٫a7[ ydP'f?t @6*jgri>V vQN-N˻z$<ȇS:00^i:HOAtã0$õL uׄu`PO h3ɾjo6G=%Zd\usO3 H5yitnQP(&:^B^%;S!>QIk^P^( ̲dЦLCw(thLj_eѯJ#_ ObVpy\ ']P~V pPEg}Uh;_J 0/~l?ej /1/҇ARP^R4A=pX9et8x9zrΣ0})G ^8 MϬ8%fTW 7va!IIF ZyC='xN@!@4Jt:m5O4B/ +l<Gah+Wba& Fe m4͏pk{ 4'H'O&Tѓ fH/&͹q򉳨]i[(/<.%҆$̚5Kz'ʖ0K@AǑ1Nq^Rz_<~5'.oRɁ3ټb~F[Z^~Ļ%)7AT$3j V|#>)< ˩,YX<[ *b&`bղ0ȱnzzcוUķhw?Ys%HV95T3Ran4'ִLjj0&ZMKƠh8\@Jjz *;T8S:Y_+\|Av6,jll=Y4$yokhST ͓[G u!JHٖ?j q@om`qBcMrKJ|\Jcrmu k64*_ׁ-Č57Էo4LckkD픤΄Ʊ}VDK]S#n¤u=W7¿n(߄C,d\>!c W^ OP0F(fwη]4F7L⮶Ԍo@Uk.~pYn1tTBK1iO)P>i QE/EE8`hT}7olϾGx)栎qtv d(0K*;BV@gP#7c!tntrx arv'f)'^K}=Kxhэ.7N0/-䗪X.#A m6Ov{LYP|bxw0qr*MgtI| b==u/׶G%|p.hyMncť,čf)j!ܾ[QNZұC/2,Ph1Mzm9f'-hr =3.xox m\g=Io. gиp85<ùFZ0vbLuq☴qf~؀MtLh(zqù%J:GJ)W)iE y&>4FPȃQ/"6aV=ID-|m8}V3*ŭzF4C nQHg$̧_%r)ֲ}b2=n6K y~^PeqnS 6q^^nǧ?J)U|h8Yc5o<}[y15X~pOSȪ_rvuQ.BGw؅.,0@e++(IYYN|/srw\W{AKw*~g-;[vr *d1Z-LJVGoUUPlEHc'yGU4DA4?\}"^䟇c*/~ރ4MPY3Hzצu6 J8"^ۊ; oJ~7{ &z_|$:2Xɫʏ_BF(ʛSmߞld{˕܂Db4F׼jqccjP&kjj#rG> >ѯjNMtG+) z"gLM= XQYUJFcDq3g6Emmvc-:t J$3x{e-35`e">>(*¯􂪀?Q,ØR;/V47K,N`|r!o .pUR1m8}sL~7߀viI,5%5csgQv|cq\wԓ4 vH|kZz3ӭ_T:]\%7ƅ i\쇬8 qˋߢbl7W9n4c'Xm*KUJ7اΛF3VhӲ>i8Eм9QӦX J`-_tx&?-]!~uyfG9 콈AW&K crE]Ը{RS\^^+ѪK_;JBi:FZoIx[z}! ՌzFz F&F V&VF'0:7dƸtzx|iv SE L L  - 6d)FX` x340031Q,(*-)+.HMfl_X}^v{Ǜ7#F xxB0İr]C#̤\\G'˳OV`aaK RKRAlQ&|\Kx;zu#d{Fe&CɌQ#x340031Q,(*-)+.HMfɺa}uk[E4/ nx;`=CӉ庆F&zIz%E驙3Irf%%g($ jA~q ,ғX場l,0QI҂Ē ؔ&qC%,vm1G7jrFb^zjN~di/"M7{{Ջs67CxʺuLh1Z..jsC3̂tdr2krf^,{c)%.5vwa$QYDd\)o=jIJ@{3'*Eh5y7'nAL"E6[&&+1EZ~u(.gކj.K-*f`&ꕭa?)<peAlUx;|qf&"HfxηoýCR)ev \i 7_LralpXvE:#闯i9y*_'b% wa}&NKM04PR,p|<&GT8k'Xyc/4/wdTٜfcs^jv)[ <@h(%7 FID)FyM99RӊR34Ι^hdTx;zu.g@PgDkd[F9Ģ #i,6 [xʺuske7:Wy; f&& Eyiz ?}2zb-Scg~xD-eu˻='BNfPv*LIeZ $tMfhUo9~1Nj<԰61dUWh| .rB.K-*f!kΨ˫:R7\xwoà ^h2O_ȨҜ= 7Lx&Mqx x&prq$ŧe&O.y%O dnq|'7jl^>KO$?(5$R!3O8U/&OISGA_K^AK3>^C)_!2T!$5$3/]$_ (-(W$#X!K,SDĢJOG=%1 J=E@*J2R 2sa*&oA}7]lSZ|@nM>ǿ. lܬ`}eճxtfX.K2zԕ~)W`bA\ä'?\qswGlt kͳK2NluN C^j=`{"xTTS(&Oiz%?@gt=UҌB۹+f\=`t9Z1(l_|q%+MLŮ3`Ys{vgxj1 2005/03/08 16:25:59#include  hotplug_dir^(\]aN؍[#x{tIiCɌkX7G s }x340031Q,(*-)+.HMfuZsB5Δbk,Yshould_init = 0ff_@ 3cZ+c\b!qxe;ӆ)" F VFVW0l+u Kx{4MiC-ɌkX7G+0o^eϸYeOnl<d)h($eVj*Tsqr$'hhZE%Ey @v9n!(xʺuC5ȯfDM\c= :%+ϞϤ^ x340031Q,(*-)+.HMf8sj'Vm%yEaxjLgB,E ٴ7O' +x340031Qtv vehW}~u!DobvjZfN*&9Lݛ@:2(ʩ]oȱNBf^f^ s\#m{')PYP[`Y˼u'ߟA(IKL׳`:-ؖ{Hvb;,qCY2t]Ӿ̹:ă%n2K 7~P6K+7UpA nqj2= .z.LPeCʻ6 Ԕ$tM6E|.zOef \(,g/ 6]t)iu$&3 9~[c:S8DAM)K-*f8 '\p~+zx;|ҙpXRH#WW :Pc:W)4\ Ce$N#N~褬|!BT6Cڻ6Nxs7I{~-O]b@qa4P=6{d6;c)v "ݿ~4K̷%-^YlL48-"#.k`4WCԱKhlwbpD p08*nkE,68->1k"!ȖP!,FAv\HѰΖl5* 2?HXQ1?+ Ih2Rȸ-2k`ZFUemXqZ0L( V\h,dUx $zkBqHIbUD&0i׆4& iև!R!ɒF(7+DIks5D_(~<]:+HOmQx!>dZkUJtpcM#ۨ_yҗz'|`jË\%\ 0l'y= ¼Ҹ)M"RuSn[ _IҰ*x! AzLF3PFK9bGi'ه-t[_[鯱d*jbFje2UUNK!cZ 85#'n<6kg-U> ;4PHA10Uᦵ0p>#W s2.~4-tr Q]ZMsu˜-< 1I }2@+ ]ƅ\Lv~ ){O*:*'(uxK(m}; ?}*c`J2?A߁)3&7pTv{P(s*)(J棉xuSq:Ш@yK8_<Αג|Ա~R±MdžK/īÍ=fREQ@1VJfP$/RG𬑆J{l%$ O0b0FHmj;@mv.ivIa٢Rxu]p=C- I!%M)pb-փ>2qlGuό+*p2H(@P$ʍ.@/gY!%hɆѮ<v*~ Ne(f*M;u: 7.藰1_vXGb odnv;l+j[ \ޕ}q[3@'$VIg*DžB#I,-::}*gy??^@%tS`[L'с1C'ş>f&[~.3?YIR)lpu?>ҡ_|¾~+ΟazҪ >(BY Xw\%מ? W6H?QB& ec/~ 1=%e5~lF2f×~[ A$vgCo Ȕi]  T33kV@5„`CCy32fnRN굝Y@,h1H±$4Jawn|N=7P>T +440<#r֖[E4mek0t-iL$gH*>]1#!5]$xZ Es 5 aGNon[臇SgZ-$(M)u0_-=er A# :bcY1&r|9 ,c^M•s\u3@?z -bZg M^767Ս9E,fÿf"{#z xnoH#ӗ!I~Xє"FCJGGs="fiſ k! FLj~fe۳?V@v|?E1;?xď];<ʸhXۛvfnŢQÝ,y`JWS9vƍ5UϘQ}LB ܫY@!q6U)[zi!GE؉]îA'Ygxm&CSFA<$M1@ #Ү' )X"B434;p+*µ&4ͭQ5L\٩dyĎ|T*|-D?va_Hr}Q!'.h0ba Xs.U_jP2^$Yع^B*Lդ.2-m?hINp0WD#4z!I1z_evp|nn4-d*drXohyc#V:'ib=xbkƍ@( cq {p'60jk31wtA+|M̟ Eo !uD=X -mZpЈCkhpA]^Bl ;@0 ?vԓ\=Jb)HSOiOYi<$LU O}@A(>5ޜ9{%Z\PEqk`g`Ե\',1_NqDM?M2'qRJW^u)+M[I0n֓ jRgaW93/ETJ nnp 3'uGfaCt VpߪxDk=g TvB?sVeehLu j4Ȁ/\٨wY3(^gϼsW嬗@W6 I>/ z?ß8Ծ E8U0b b=L1:%uAz/jcG 5ȇ81K3}Sرx(d2C|5kgMfσęJiS>Sl5>CsqvOs<'|7!w Q2KH_x~@f?!>#&wP\ pC89iwc2R<+m 6Q-(?;N8EbeZd{߇qm_b9ڊփtnH! Wuh^`#γgT*Ո4GDZ5ekn "d0S>j7L~:g^Ϙ[dsu SZT#./«l| v̗&+Ҫ2Bl, o1e`@PҜ/ 2֏9t 1([>f]_B4di*1+xO>ZPtA +SfWZJRK@9i=! h(Nc5,[D0:44}yȭÙ${cI2\(0`&+EI1aކ]@49ަfrg%,5/wG>(z!.GCѭz^*+H;O7^G5 =ԃ:Ri' kF|&3gS)V| j]-I?1SF ~:eAhSzef6Clf>5-"<>47Z՞RX39S'oڔ9珢ÔsZ.u⋣hBuLFL>XqlQU;3ąVE l,oLrvh:zMTYIST~_ʷPt7Ūa(Q'ﮤ#-U,~*D-3NCH- LlӵefZ4"Q+ǟ2ZFvscxk n P(k`d``o`od dZ[*$eegng<.9\X_ 5 <?x52P(k`d``o`od dZ[*$eeg.(̤i="r1n>)v"1=VWx$X!h(只d|DAUx^cIwE)N/=&߻'Zw#~ۙ4 6zkhiTLC**rƁCX I(fqrSa^Vٰl7Mj߼LyK2W%XgYƣ*SLpX;k&c%|JJ"s&SITr+b5%XVfMygJ-M [P=ɻY6XHidBҹ'-<K,&fWx|/ *|ڼDd;(M#kXӶɣ^ S̤-ĴHLo 9`Qld:z.2'V>IT &V^ BPؙkN[-&H~_@Eo5&t?+dosՇXNw X!VLEjt0C!އzTQ[iXFGEUkw %3&C!7s{`-;9N|6s:qua峄-?"PL<>rȸ @.݆|ltTCh9m7w!hDF3c- ^IγdsQ3h;M="C/S3wx-kQę55] i<}TI6h"Hۓ5yf[[<{(Z5dxo@Ǖ(rk'e$@Ũil*U*THW)Q,>'G>0!&dfd?`/`.ND޻{{Oq[r.AݶW-n/p}umݩCa3z{}+@I>͝_+;|W;~z1M.ե pYcҘȒ/~76ihBŪȑRZ-ID;n5G(F B@ n!թ:{&"n R%2{FMy4Q`fB8 \.UYVyk۽c l<@PA'\+S4>/Go/ FEgLxO ~2/Ito[x340031Q,(*-)+.HMf8n'KL{|}=<: xj:IgB,l% Ff7i1l~ŹZ X/xs3[ۂo6ohhV6G$qhnξUUg"ϑ8|OzWX!100644 iprlib.c .Ce1RL43U*x;(WtC1R֕8h uxw# W5L^zQ~sf156/蕳|X05Ͽa #gWrOj3- >Nv>k=ʟtk:%93] @I1&[+duL@w,b 7;bz^iF"է=4c{idܙ_x=&ӒUT)x340031Qtv vehW}~u!DobvjZfN*Ô٭.T,~{a/;gj\]|]gF18'ė\ңB{S4 P,Kap2`{ovr<P#2 r,w:T1r>ё1%yiz "6l=}4K;>],AeSakFB>lπ .='̂܂gy&GN` :x[c.#d=pπ .='̂҂Ē͏'1NÜ=EYOkr4gAQ~rjqI,ޓ+5NBxMv"_>8>JqI`;:8UWpm5{΃%*ūrb3ՉM'`:xΞ.z1O~i_I_ xƷo>,"< @PZRZ`09359#_A7og@PhcnrFjrv|~^Nez@*J J\ #2 r3SRJ RKR6fL,39Evfu*  x3L ՌF FFF@BRVvf^[&Si~ubnpx;zuC#t F',w)x340031Qtv vehW}~u!DobvjZfN*SMO _wkg~B:2όbq"3N/Gi&@Y 'aScpUjv/kfYP[`Y˼u'ߟA(IKL׳`6_yeS[Y7 e z';~էiN}%,Z_͉7Nؼf팦[R@F͙!c~џMj%#)-9k-iާxa9y/di**}f!*|CJ#?[6¤$+\"f/2`,BJ}1vkD)-HI,I:|o_,[%2^x d>.4ӽzڙC{|$ R1q7?XppΥr)eEřyz " ˺P7Gy)}"xSxk!a-D0jd&U*+V$甦*8+ddohdў&2^ ;xΞ.xJ-gͫ?X=/x+ΣUL-j^L4E nw(fEJPd5`v٭#R~/xc)y!pױĩ0#qP;ωg|P-5t!#!!DmR<sPq֩1 D` f`&Ǝ$mdW %3-GLXO9-Ndxy|:b=Y?idPˁtMU99,olj6"٧\x*~$|NASYgRT:FdmtkyYo_vS"GFW. )(E Ҷ^^rCE߶/,~"ӋWgRy`4w7&ay@sxԃ@|>^1es㥅%# v`[1L{r-ocI r'WQ1{)=<B3axRcԔC?YGYx{%s\jCieddeb2y2{Rc#Ԣ<[Ԓxy=, ,X;#!$x340031Q,(*-)+.HMfҟ1'mϒgO՜k6lx_{C潬! ix:us*R7|2i?~'>e$O45ZԮi_wBfAQNf^CGjfeN{dGo^俌V.>Q,gifu;SƦhǁ2 ~d=r hM0@&_sjKq\ :B TudŇ}h-tB5cRZ.;&ZznP OuC<}7x "k͊ %< _NXK x*0gWiF" LRQ>k &g7ROD)%^3cbkh5]Dn⃤/.$xv3>q6RSCm _8\s;xNK&WTY4~VXP718T>k'f۝5iAz$ƍ[ T)5xUKTQY46-q4(H(t8g69:ɀ"B`V>F .zP&AovBo/~{)4tvs|]u&56 u$N0,emmR>%KX^3VFxVs#{2kU֒jq bvƆ5^%}*/)_: lUkI78gI,fSefŁ&~؊V&68W^C|b@H]"d8'lU(KN}r5ݒ,]Bg vn)%> 䡁O(ZJQN(\vX#tˁ|j(Õ,j 4j JA͜@g8\aHWHUWfpG:(>e ϣ8+S;}ҀٰgD%?$nyqjC);5jKq<̡=zU51MZfd4J /Qox7S,rlό[,ԞBL%ܠ^e@+td=4"NEԫK NxiJ SE L L+##++['Ϗ,;W,.2\Z;&'tpnܡµY`r"3Wx340031Q,(*-)+.HMfHN3YJ%Yf.YlxTkCpx;zuC KjrjnRj\$F)x340031Qtv vehW}~u!DobvjZfN*ÉWؽ +MOB:2όbq"3N/Gi&@Y 'aScpUjv/kfYP[`Y˼u'ߟA(IKL׳``Ii/gϯG+7M,mS§b30e0|Ӻ>c3_,% dԜ~2!:͡V<ߒ}z&)Jf9׺sLZV*QL*275)U2{?6NlPxյ'V#LLY@wģo&_٭j26m(ܹ&fJhVxDbYA҂ĒTn_ U!`(Kf,t݊X϶Bb 5A5hӧՏ}EtI)eEřyz 5].9,d Tbd?rxk,a-D( ̓0jd&U*+V$甦*8+ddohdў&2^HK>Xx,~VbCnjErNiJBAbIrFjBW9$3I:&r%=x[mcA7(9CxcS& &2Zl^)yDv=s 6x}xSG73r܋˵\mJliBX2%#ɘS7 efRxlw) 즼 dKBx3^IWro}s̜9sN"⁨C1q}ki3WZ.zK&li-BfJ&])OO*|O**i&Ɂ[w#u_3)SSE'C PX,qXB {Hr;Z܂i:Ljw?*c1 1TC7 qS ]crQrNj7;6umr IS']i4kfCӼY3e3+G2>h 8diaFDajb X|6.*QM+lUoIO474&:{>RlG'}rBT)|FܐD4pM:I?R:R{hvcbX05Z) 79uF K6,k,6Z.4w~&QD컲贉,K S,0JX[,2c{R-tpv DjHkRVԉ]`KcYcuDb0 p 6]~ͅoVh8ppQDW(}-e/,Y$/K.]EEU"hH3p s;8 ( Cî()r(d0{KXEs{ I"Ze\uu05nq}(:2HKtKj]n TSZVwYTZZV]ז5}% ͙zt2!66IC8q(O'R"ho\W> SUk{(X}?)ϫ7p>[E/p"PVRU[U|98f[Jr6ESd7.?hK=hA٣ڏvB? Q A P,t:Tad6iiY@/8¡0A$92l(*N~ ܦ oim14yNGKs -롾u?·pZvϼ53- ?]&>r鑇bǃfEwݤ߇?6ltcf9:f%G'#'z޾'_C $EKkFMǴ}w>}ǎh5GBisMg>;x^x=G@&3pVߥbh+{3=fDgߛ#}OztW2ֲ1: B>8ɫ(hkys9}׿_ߠƷ;vD_ tzi145f5[:̐}/Ft9}-4_^x ](py&v5 齁'= qɂkPmCi=yZ1ӸŰs<RE*܎Ncfq[ZawM G|o<3oM-F\XP(wL7ɌU:HuBñƢb1ALg}ͯF"Y/q*:ǣBxSGr|Rň2x$W ɩcQ].yiD8<ExAc(aOoDNj>e1Mn$cKlg(iO'::?qƬBO,|OGJ8 $]+J`- otW;}!L Խj`2억nhlF(u *g]o<^F8rmMxt1w:>,:ɐ _8t9#w>3xW÷,q/0)PR.s.e3{QhmRnr];zibN!he[.0GQ),FhDwwx0ElY2pDQsVHqO 6B-"ō0,+kh>O] T(PyK]WK_B PNc[^ e$p%.kPĠDł'>u#{bQjԳ0Ѣʦ[,{JQjTP B9 x*BGzKa> |0SQr&e0 BTЩq_ԎAbaي!f<%sxt U$j*mPQF9Ҷϡ b B"-̨Ee\%oE9.ipS' * ;LzLlfFqXf_\[p6<45 hM[I&߱8ѨJ7U)c>H 4}f|JㆰTe 0PKu(kA}M@]^i~**(WƓ=5K(] "q> >Mp#+|t'Lefa۫f%q9<RMh1zC"񿶪'(!Lj.p\.@wj)>c'Қ r4˲P%,aQX~Z΋сwNxxAd˰dLjAD|c9x 0kKV%E[LkBpU7$*dɿsZ3Z3Q8 á>:ڙxyˆ2$!)` ٭Lp#mI/(ӭh2BqfME+ jKP -I8nC/(cH6ZL-mFEgp.b1bPsThٖ)zˑ}0ee&'P@3iX^`;!l&=Pv4XG>-W %]!bI"hƠ?!Zf_zj(U NauLB:ElL` y+E)9 qD6}0.+Tyn6 ή^4Q!@o{r:und.WEYg}72@6@I&?wfu |ơ;ApQy*aKPz+v@-v? I9nf[XK" ?obnzk7jYrv86Jt8[ +[zttyQatKt5df$nD(93.@F b&]Dp|#sBeC(zJaJa 7d~?/SX8Y]C<ѿBkqev}!Tc*_ޖTD@SЦeؠt:Aux@ nÂ0ɲwwb8xG< }w#}tp lN{d@At}up es Jڧ-hmI7Q܋48]7s1p>L/eъU"*-ŞITP, 52zf[&S\1Oh"OmJS bY"ºh\Fs֕%h n:pG@AMG]c⟞>=۲[϶3sh/toqB3%l4xy&v,dUgE%v9]!|UpESK)xۣ([O{j#%,T|4(|(X6L4JfN_de-U<݆o/2H^B7OTv\mͺ-03]y*X[̺1{+44ISDl/ W7Μ4}͍L5 ,.*<\lܓ-tEmP+ lK9e[S/\r,6KQ2\VJ{I5T:Тm5%7G(| r X`(?4(uƿO˹?~LInwYnND+S{\x T<,Fd~`PfK ŲŴ]$EiHR>$2”QNf '+Xp`#34o01eOʀy/  7PaQr؄,8^!8**⺁{Y2VM:&.&SѝY 'A}NP&yw 4iaM/g8nU*5nЙ-<@rʂڡ\m"*}7#jG.F@ T|AG]}m1bwJ\ /4ivڴDr;<{SS)Vk'Op?Л⣧%]6S?~eaq)O>Ӣ RЁ& C[UZ*dA::iIJ>j! f{V+*V(VYuMêAB=C2gɳhF"N\R7P] w픡KS4 eXEsRܠ)&VO J8`W}X* pHύ$c8p 4)Lqm>><|Wؒ.s(= AXTՅɥR˷y&]v+R$*nE^g#DmHx7g`hèҭ+JL)sR3Z%l>E}{TV+ڊ zU/ :h! oblI?"RQpq䊄2] 6႓ha),S(G'<2bcPReb0֓v7UP7[%#ǰM jDeCR…̼ ˿B!5Fe470ΝW?o\YO%(G ۤ5ۀ xX7m£ 8!x^Ȣ|ڗPr$@ $7C6Z;ir3c75Np>SB:/e=>`D ^ U{~{_Pb}V mPLQЋUBwgV`=F(Z`k.JpZ|xXc```nT\D=Z+Ʉ37A(_ўY{$Qs81RiA8W_!dR7Bڭ(of Wsr9܄&TȠ0gOy~T/Ki?$UiiG|pEl"xGz3pX;(+x) q㫰6ߖ ?PMbTHynn&^lśx,OK~ X x(\wfB?c]pţzLa-ج8ޜ𦀽cxja?T;O}`g;=aӇx7|q ܱсb>#F*ŋvzU@ʋ7hczPwֽU9LbA9x9f^⧘tA V?Ya'loCQg4H?Н(/N(CRo}m} S0axyAA?(tTrAlŵ Y! *ť @Q * /zk %ysӝ6ӽ;ldFcؿ_o*yH9{ ׉%VW;}x\ }q3Vk|= ϯ^oC'p'-Tī8BqΆqӅ;!m$U?>W3 _b82n ;PFԌs][~'F2Z($36л d!-ST?s~~ ۅ.C4y'z[ S*3糳xGCO{8H3gPI^*(xy%cyw8J4!#,K٧lJ^^IrϿP/Gڄ+m_>krG {DNOz^dfM$Gw$yF1m'MqN^%8=|~Tkq钔 EGS`Yш/!$\*TW cғ4?!(a708J$tSEpL4j WK%JYS@c8kJ.Cd85U#idaC7Fh"r8!} mTGIm/e잇6֨v`$s^#Ip&xLgYц  5$œ?ć#J{xY=juFQO~HG@7T3R&ǐu$U* 8@J1M㗁G\]>c+N~I4gҦ\rY3iqtr㤩6ġi>$% OrQי"$$uo|$3DASxbBJe߀7[?kGؼ_nb).J8{O E}+|>I5B/:⏈yo~EϾ^ProPMDFU|٨~~5ԉCw&aL#{ 6͚i;Y.iM,J*13q2X;JdU|V 3yZ,0"33)o&3-d q+ID2c+),On%pT/I^f; hRmSwv)(ynZA7CU?F+ x}|E7m7 ^M#jhGJR%d7n(*gcAP׳P;O,Ά|3oے&z>~?`ߙygy晧ϼg|WH()wVےquxD^`wZDiauv^BOqؖ6<4v:EIuFhwXVq-Rub2ikD%o ˲ljuɢrzm%6^Z;@zJj,5ꤚC%?t0ӊrHů 3R.9/Fk9r.^)trTl*\0iRgUo2ި4z$jlD# 9r2v̺ zf{j鸦Ic.l.WOOhj9~j1$9-55^ְoiPtІү VKvXf55b'#th)V3mv]`YBqŴt^. pA(Jk锈Iiû,=q&.15D 8ZmZIB:ߗ7թ"[ER3'%1fv/#>.Vd?1W&~|ⰵy;4vLB)_)cfiks%:[[m iLi0z\-5ՌPԷ2`z^fs-<V0!iRl}u*oA((#CHxkQxG0 (8˄ɘqY쌘盗Y$VFqޚQ$[ 頗vŮDCgTH:aeH_)뇼mDzuLJa*ikk5*/aWFM^78Lf*&G'HKD?M^.hNYOM)^)Τ'K9vmnO{X*oZغ-q Š[Auf,PP 2)Z!H Ї䎜8XRRI;g["`cⱉ {< 4 lZlnŵ-ni۝V96veC xP尸Z8kюڼx<.yɲ:(1-nġry É)6氆X<N>P^:oT,lj$lL2qǿ LIZSMB])ȆRYZ} bI7&AOA\US'͑3i8zqe.75HK.!M&2 qbhưNou+{k-+[@keۘ4X`籽HJ{>K.ThZ?Nq^6g x^_!gRg{"sd]Dls:;иJe΢RI8 +QZ0LU9a[QZNg{QctVKd|KA omx;aj|>a l0ڸ"CReU:dHf)s#X .~ WLZђ%oџȜţrv}0m6^Q2u1$p 1ĶeI C!"kfleWKbRx(XMΎcmܕíqQjV ڇ:<ҮmRL]yL}ҩ?8=31af%ZwŘK]H]"*ɤIZV3ޙ{O_[%~0;6(am6  t$1 C?Ө顜 g-dcsHDwαn"/@r6;|8@mq~4lcQhׄ7ABV7\p,!J >X8k` DZ@T_` }A4q]°8 @n3 OlhnE8.J:rnWߐ9zieBģ9BzNJ sƱZ^fV& 7IXyc }q weתJoy=I /05#tt6=ޝGiADo3h^r.Y:Ӡ9kw窌,I M%&--%*vLO[ڔ=,V%ĨJ4V]BtY -H]&dt6^C9 /'Iꆞosv^S}r8@$ jSI|txab-E$2M h "-P >i'ʊY43;|?ч˧ȖQ`? ZCn 56L)*1OoL'~Ф Y]z*eu}c ,/$eKC2ʝCQB,zlCzj{z?seW%.͂&%!|^ڌL:ۜ&mGɚ,}d-̝ndg1s!6t0_ {P)tѭI:^m/!c"nR8~TA /b,{g34 ͹%"bFbCIժ j&?ϣ2&Y|]H"cej ijm~@׼"-VFlź|!˿Z8o˦=PѶ\?=}촦IY}_>NBaB6| ͘ Ny0pT=fKJke׫4 cX,JjFc(oȖ5||*$jrE@w$]ugnrYՅIN277[S ɘ@^evJ tmQL(`|m  )wP[ xĦ|u6tܜ.M߱YssvJ̼ujp$Fl),4EleO)W_9@)I[ t˖>rYcs-2˶J/r]EyiyiW!*~]9wJ}חwJ?T ta w'㽿'nf1CMPSao>;ҨECfgG:5W8F0)1cF(s:{.?K OpDK1=yKҧ y?``f[Wh_(5N=Cr}Q,YYM$p!=tDJ֚~woBᮽ`ᤏbiEtL|`H_l /Id2(><J{ЧM: rjT;RaCư1xnG3D#OЋ_Op̽#[*Z4AȖvVP7N}vydU-ǯPCR`8MOsD) }s*{M 7*7Q[JUŋM:VY9c|w8 Ex|XW2O翓WcK@Lz lt %0B<B(PXFϹd(=/o0I_@d kh˸AX= 3Dt4AVcXN+)0$[I&6q*3||;^e])VCNUKg0K̆ʠwO̐t(>!eqhu2zhh]K?+&8I8H1!׮g/_42J2込Dm"'gJ/nj>FφBXA?8Q!p)+>:өhy5=K7\N8:&f󷙪djRMJTM͖?R @WUJ t ΃4j&m5}0^Ɨ%=vqhYR]ҺmIG~tuQ2 UL1&PrL]DA dǓ P} `qdę_"DzFCC2O":o };!s8|;={I(&B=A1z!ϯzF LMp5xXMˀ4 C"TI|ZHЛ vecmG+3grVm]*L`Vjdguy'׶0[[r$y0 p"] @yg'wګ9[ph %i~EݹUj 12qt$ikI.)Zpm^͊{ ^7hDS "WڤRkxKs$ />U Aܢ*2r x@{C/ |Or`IH"?'cGFÌx&[Eg1 lf ^/{^F4Mc^>6ڽۥ Ȩo$(^f0D /esH~Ў(zK&|C YH phȓXSbs_*xe!WM(Y|μ FiAz[Uà{7̄TUqv!Znl)2F=DmGvQM%n#+=\suZiks.YI2,uG/4uS"xFb <p*ZTG$vzܹDǭ$9GKH+^)=*"rc(?,go +}|g(9h-x({D[A{܍ H4$.yrpF>.4AQP6f{whL8ȸǠ0 %3cVdؙaT?kL:RH2=4U8x7@,Ga2da.*(8iDeMt9èB,h4!gogZ^';6M:˓C+}k+wұp\T.:|YKCoe%& kyxHqMNV41)5YM0_lbrn+Rm?MADסٝA` 6m"<platAc" ]8HދWObZ,41_``'J,7Ȯ M( v:FmN|S`[Y鑬w G2T}qԒXS8r2ӲS&5bᕑByfAR dto܋P Gs&dEH!%uA"s|T {~K>!`txH%P4N9 7Ξk%+2z\v5i YYq֠r(;;IZ-ݕ!,)DqGR0"X52N0zaw1lސKsy/U-)by0;Vf%Ҥ'nob$%=xyFsr(kpldVώ,/&H㳠!^1qoPdUp.c=!1ZVD"Sn2AQMtmr,.a6ρO!%76#5O氉?)8 %&);_d~#UfH['IFJR<3rmV^%2Kz~!+5/Z!dbR^!+ącYZ5(:fmH|`9˽(đxoLzTzzȟђJ<Ҫ})3pK9r:M5YU~ŸʔfKdhEcd˿M 4N:e,(5?9^7 wtΝ?`E4V+nؿmW5z~PlJxt.<˷a%_s6pNRU_#6 r4{ Flv4dedyɼo0Ce`cwz=#{HavWF$IP['1t>Rӹ_snkeH ^^芣)xYV 5z6x׮{!0痿Qb¬et1)\55Y^Jf]~Md^[à=`&m!)xp ]NkA 9 2 =JF[! @"J}"'@Mߐ|y 2člپ#LZEwt1c라D"RuV$@z3 5~?(R]2Emc2)M˶%۠O91<#kv+XrmsK:i?4NT9T~Z]%6֮Mr~w`&A09YcƘ?ˉOr/#q/I=d?[@+n|8_?}GB]~1"0"G=2=Bcq2WE|2 c`Uss/ }Z)dBNw%p$O` w+ID۞3,?d.|} jH*'10w|d}<x>k/ꞁl"B<􀞽ٸN !<`.垽AA.edkAa m!%Y  n8)C/!Xt^5DxOE%ٯF >b|%hzٲ_88euW[{ ɷbua؟q!%g~) wE=2ճ"8 i<U$':^AfBu4PfpJ"/;ڴhNji髯Q;qf2Z ) A  9FJw,r8"y\HG[o.ƴic'L K?_uz%Dl> d,l%{- ]0*]!%aqȷ6t- ѢUc -U:S6+asHRcߴxlVqriFKvV^/5c{V`gb-zV{vv\ M ʯtl)6JĚK"?Ucmfn;LLVDɋ{54B9^҄ / {(T@yb))AwL vɷ!KޅO;}IH1J.Bγq߸b9n _18xQHTPv'ݥ#Gt^Q&ϼ R#눦WE[nk^lsJqk.3IMob Ƿxڔhl/.˺5|o7oe/Gj,<8$^ ݲ.!XP+.^VkQ9X+U>KR\LZrlS _=]`Әql_ G0ﺴTGT)wX]Lꕋ8$ W) ѵ_h .|Pi@OT]DwN TOA_PxUs6u^CM!4tHx8ؚ2~?anCJK\O/ji0ŨE0C`'5`<&k0 aOr`YrWyL0l#4i`ڝ>·cEwҨḷxx,P>W1oUR|Nqn/}QT ;HMMǙ1 ,}PQ/q c1j^}O& zy"gǏPZ̊j}aY34U Pvg`|n&30@zܐERdO`r ހaBPGM}(}rƝlN e g^'xqu )* ^Q0dໄdijo~1U %?٬dRmITrpj">A H iuTr-^\uUB6E 2_E;&нk2t<{`U\Y$+ƒ1KOO}>L71Wd<@94`&L'  w ؑAcTjg EJQ0s?Hq0}i3~IFk MWIN CbOݙE.> òIלC6UiTor>KɌK!2h'@{FhM+?UQyP>I*ZQP[RVWL;a7wֺcM18 ?LHrX=7ݦO7ld+U 2B_al9W֐//;0ktn 5$u7Jp PMV.u5z-M/g`C-תF2L;%Yu$!B5֑l#2ebѯbd?gN'[di$<+ӄ,|tB{ nxtUl.~=a\L䌠ׂ; B\zS$'r.E /6"2En"t42z6LTCސ#dC VbkXMnr!E=npR?KD'3?;y#:F]7j dЭ$aVR2Xn!7sOn ݜmjl(|YOnW?. *!܄sCXҚt=KC|B>4{!cٿ^oS$˃>s,T7Sdho ݤ3u 5zվ_Yb)H_ ϓ:mؙwdϼFrL}㽯}5R_y?$D$ ׎~BɪwTW* /]TqhcQ\32Hy3œߒ!ß"Mdtɕ8<bx!~$c 1c]'^4 7}E,o?Ha:Y5Y2@7ou;[gOs{2zţ Rr,yAֿnI)bFI:A"]"~~ (ahK] ZxO9~(r bI'}Tͮ.jO&xwDa >IZދS \Dяd} Zf d*Oha9bSY7قbe$lrH0JL/w ?vmW mٿxw b?U N[_p[We ܋ᢙ|ӒO~ɖh&y;e_$GƆ<;T ^QLl\G:rÞ0=?N$0iqfOXB;.4AŽq*T#pK+ n^G_ɶ"OtRcY"]W:=LX1Siӌ2ёD醚TܸǦ!(JFg)X~x+ҳD&@K*J1 xT9ehw* WzY\Fj/[}FU䴔YLP>ᡶlsF||,b*"@rRDx$[9L+"-Q9xudBT#UM+G69!kcFЃusG.lH7Z V==`9)b$6?mqL6YchItkz̮n7g]tSq4Mk,{_YK6^Nc ^͒#xz6=O&lo' w$r!OjX6NY]te@cQYNKz\&e⛦Vf&> .ZY^YLVГ"髫b鷫". Sjle][Ʀ#<N WIĬ,MO(65 n-]"ans(K>lg);^-d'hEP t Wܲdz,65NG4)':NjW⌉sؒ2e+)ܘ\?EM^,<6H#Rv=K" Q|^t\3ttfU |̍{X+$!GHRX\K3_6xŮd$ӹ|>HF3N ^ERӊ$R‰B/' >Ir ,h?I@w]K,0á_L¡4/kiE[5tǽɂ^w:73*ОkS9LWO>cǦGH>D"_O#2{KIntӜؑ*҉Wbc<ɢǾk5O'?8{}=KJdar8g'^)X3B72E$8i y"TG>Wc:#V ׀GIMy$X"XCSKkes+{66)M쵒vOfx.akӈ#M$/Gq}I& s i> p-k&E=ɔ%vYDaDڕExg>'[$%o29Vtdcc7Ra$>wtZj굊=.#:dq\+8,[9rHAH<)c]ٌi$Ll$G0<#Ev<"󝝝"w. wjwEG_,tN+{O@&2V| )1H1XDS|e`d׿Ĵ Ωm3<,繃[tPo5{ؙҚkL+%{mב|"W )j5) d* Y급|Er@s i""!7W1ͩn'F\`E<};STO,NvZI$DL1L,>"6YYavTqAܶ9Piʳ+qVz| mG^rOA A/,I9Xtqs?M)JH!InnkliP]\v8Dk2kg1[kk56TU56T3Pft z#>X%a[WƹwCﻤ.k1{n%SMVEX$kSBHtvӛ$NIfB<؞w$ibT`a_&s!Om_^GW \qЇ'KW~C.4&Jf,Z xj&:L#d|"7'i1ɼfeg$G;u!JNkΐ!(m õ%HO}K@ÈI4{LF[NDs& ^KSM4r|\ t{GL0kfgKo@hǙkP!Pv0<[px]nr36i­ vS$ȇK~1i$M,g+, /|&cGyŋK$%B"G@2^c F>~ p#+}ǂH=IU#3۽bNZܙz<.< Ƣ1kĠWlw 2p¥0IpѢdcH cd(c,d  ,Aw3Qv=&l̅C(, ^5>?QO@[ق*7>` 5x۫ŁwՀ-e= lt2?`\3~bL=V殚 tApDMcs}ẽq)O2Z[aSsue(=zvonhBkf6+#/!cMת>4rFm2%z]iz]_𑤸@~YXqLDƻI] ߂EFKgD|=p\7@ >Dz!#n1Ғago0F1W`n2ؘ6a\e-hԛ!^Zu<[ zx|?KEbZVis F^ťݖ BQQ%wBVyC4r?ܾ|2G,Tr .  LHBg?;6:N~"}͒T Y,6/) Q7JgLrTUYqo]ʖj9vd'xK,cMk7L[_@܅j/bz<.a8 wZF_lay]f\oya@Z-7)UULDd"ز`E2er9" !W&YMum3R@>Q٥XlyƉskBE<\6 %A)ByP0 dLg=P"3Ⱥ2P7U( Tͤ Tsiͨn\u7} 7<&|֓m7L5sL~5Vk<| _%פ,i\3n_㭏&>X(gfcc7CATgIiE u L-k`<:W@R:,r秪ʖ*bQխ3dԝ?XH%Z%a[XB#Q5eDu,隼cwܽƫ'Ow_qxncN`~}z\ui$c!;! R\ɡssI:w l;r9>|ûLWq7Hm /Zj4G 3a.׮0eH isӘԪFUfPllecڼ.x Ėl(q% :T^{9m,=姩 ?[s9g]Le ]Z̾{g!&_] 6Ww% ;!Ý>ݐV>̏<(Q\8܇3vx4h~>F)I8 J`E+HRwC,-\2Ra*> Qȉ;a3 ֵ0xZ x>FtXnwvAX}>&Ob{ן aga7Y$g){`Y7bN=Ohp E~)EZ_9.A{ܲs9X/RV~X)t]C˒;\LLsL6f. -7w؝fkY /P==Ky {[At:`v,tri0.sc9W{ ׸r "rvv5,߽yY]X<܍G—^-+ĿfUs9;|/$!׾Qo îef b7ttY]\&E&7okⱹNUJulMnŰs/^^GY`- ЮɺDj, +hQȥ M{_+BwUә*`Pe2Us\T|s'tBw|R2t2-63<$ X^ʫ-}[bD^-J{ᘮ+oAZJ{C'` ݻ jI&Mxmp$' >WgH8iNe":yRD_(`ȃ̓7?|C]Duu#Cڟ q*f xVuc\\hCI=2Ear\[yBdtV-8{2D<^|ɧW?L |n64D,5GzQCВ|Ž͢~'ݘsVz?ćB65~<6~(M72+L3\@V(>>},J ܏aL:Vfsya[.Xn=W'Q)7מؑ!02:+ p'iQȂpKN@ q>|Ax |)+A穋P;R{&-$)VgD,Lž/ϭ*A=sƷ>O?çK sSf۞q85)508UUeKIAl:2-:K|)ʽSǞ .-W.p$&Hⱖh Z`vK.b&9'} If4Dp:xU{lSUinmƜxcmw׵cб=: ƼtmwGw[cl(;5!dF@Hx(e`1D=cē}w9a>up:\0rg T3`9/ȐaE‹\nSGe+\ :Y!0]`ѭ-:Y] X*PT8!D hRns= pM`@2Jp2,?p<GVQ ܳX"'+-y`XQ h2tra_(at:R(EtB6H qXDS_(_ qmDyAդj:d2."7ǃqǑ'vPL\pF g3Mo] \ rjB\KQٕ֌LheC4 47kn[3q!{ߤ4Wc ʰk"|Z %Y gm"aHtIcy9R里ozuĆ%SFSF*SlA"S|&@hZЃ%h·^>V%S-qr27Hcd*Tk~\=yK\FTE- 11yےGgмnj!K9\CsE$[1I99ր6㳝I{/#<'˼b 1( B%NjkKaД!r` N常<}Iz2qaMV!#(~5>/ۣWrcTUN-kUןu돩N x.eʼnzzn$Z^E,p/$+q8> Wo&ۏ|`">:  ) !^ve/?2RMx0!Hi"J]{z.?C3J0ΰklz` :A ߵ2MM㐋J懯؀q•if q࿚ % Ls[Ţ\0 ^0ON^&b GNpuulVLE!. i`뚺Za*9.9kAMO&O!!pE)靸{x!1XQD)Ij/ŏK'6g~>SjbuRP4A4* gut7A6}f3=ؖY4f*|3*uex Wn^\oFМ2̂^935Z-95./lXl9q: 1d»Y9XYgu]V%"ӣ9IOra&kTJrL;)-3ayUJM_3i\Τx>Ϥwu( _pb240}>dkqA a֌@V3Zԧs0iYw+l`[m])ʢQJgk"b\1\[&%eKZP3foej TCKj<4o%54ՖYi`2S'D_dTTQΕJKKTxg[ K|(k(/ADD&8I]9"ޞ=Ez̜]C䰴@^ȂUm#Ul8l:>|Uw33Itބ7^ڠ -N=!t9' pQ< R҂_I"JOc:22zDXy8* Q(Ƹdic-"ڸU:Vx.nhEEr N7_Jm¢mf87rC84_ANcYSv;6}t򟖃JfpZ{T+2;9:'Rׇ#cWD`Eq6JEr8ir(q׎ Iwm^1Nh(wʖq5@ɵ93ٚD R÷~Dt_Md|qg6엦cw\4yQDd\QN4>$t~X@;І ^\ؙUjŭZT<0Cص\8Jc _* i(h+O,gt?C0^ҩ$$6.e69Ң/3p_;llZ'Nu]P jHP\'}PpP{ݚj.Zqh _4=5FEW7o(lN BSQܹ-C+ʝS18 {~Zد%egU+qE{6.m1ex۬}y y)^'FK+ŸѽbJg`\kp-+E/N`F8m'cd>q6h Iˎ Kh4ꪏ3j<^D9>Cs:k:Hpx'o]Ing'NR&FculS/lDL^csMcE k>l?}"x340031Q,(*-)+.HMf'UVeJ a 3Sx;TeB,ARJxzu#dGFYܤ"##I 5 ~xQ3Ņ6(XLØgG$. uƷo!8DgiQP% ItP100644 iprlib.h`Q⻮Ѷar)Y:OM|8ԑ1i $M100644 version.mk1RbGFmoOyHx;&-)ʼne)9sl l SKuJsRRRkK1eeNodTgܾq=#j,]ؘc70V0NuQj.F8%;41QuɌ u>pMha$?,\y[EX:=ut9bgao?:q9L/ŊSSS+l00VPSQuV&A ;اè;F=>M.NN΢ԒҢ<߷{N`n)(eLxu3 2004/11/12 19:05:4HTN/s"9KINZ0X0KoQ# N]*L?R0^oR)w^[]"__ͧ+xkd:ƴa浌&7Ilcp Eܛ#5E]x340031Q,(*-)+.HMfsoo^Ӄ=iXmAu#>x;:]iB,l~e Fla_  xzu#dGFuv##Ip/9x|c3ոI'+–dx:uQFݶ]Oy91khKr Yo)&x340031Q,(*-)+.HMf0fСAnSNp9(%x;RqB,# =Nn xzu#fG~F%S x:uBƘ_{9}fGMNs\ah``fbYP̰&K:bx̾݁[h_&a*uGϊy#rEC9*231)L1R)>qFKtlu UMr_=vws$^1sA#?֑S_ Ln5>|q7BqAj2îfn=b7Vy5,83?O/73qͯj4`zx340031Q,(*-)+.HMfx[5 OXs~?/J"_x.,V3WSep 1)13 7 CP!oqx;zu#d{FY^ҼT###͹Iq 7x:u%(]IӃ??\ `h``fbYP )mQ99qՁRn{*Zr`T3lO*l'1[phxvKWϩ#BhLZWqKSuP37/_ժLb(Bt(&3SV8k<,Wv`el9Q,&5^X"xtT8~fު쪗U/O]B$@  ˢ]I+vW;8q!845|v$.Nb'3ʾ/ΉO)oΝ[ΤSO5pr+s ,6q u{DᴊJJR[kXg E6{c.uvY&Yf/5_|KjՒ:>8phސ iD4I5Wal9lǵuj-:4l,"V]gu[-R8rz,}M>v"D'T&?Gn=Z}{mNFiɢ?/~Z0S8oTAt ʌdrh&#b tGc#1FfH?"3[JGȵ tzdB\gq9tlʺU nsxmbevk&(7\nq(,VͿ{p #K,M-bv d8?TlbYO鰉e6s(+)2/an]> ;lX(^):m[}^8=mur)g렝q "\j8y. =>{k|vgID=(f)OSgG,*Jf6C8!/ {-n2ZDGG6Io]||tmT4`lF͔T 26|V7}KkWWX(Fq9Œ-8{>:OXblN_T~P^/U]vϐk`XSCTn+?7)ņF#zk/­76%ZPD @8bV}`]q(ͳt>gbc77:TcuK)W^/|+~g7ϲ41G&Z|UW nXZrMWhoAS~Ǻbύe].,^ŦFoqxLeUku,m5˄%Nӿ),`Kv sW `bo_)LL/c3tmI~co21Ppu8leJ7D|g pCF>Gƽc>pbtA%(a4-6JL+Izj%ɍʢ%npp!Z2t'lj&M;Me䅦rri<)^wbr{dp4b"O!oMIpf`sY7giz<6cr3^"62BK%mZbA},d,~͈6wr4[Bݹyk^%$X,LOf|$β,u[șG)V[,*Ӿx &wN!ysGa#$*}hX@~ԉoyIdئl'\RJ铷J [Z-[,n.d*1^(,<]#$]/Ak&6"ʐ43k=`=6-N wlWo,/ Sޫ p:9r..Z̀L TAqm !Y "meR֐cHv* Z::\v[][)˅tFX'nx}QTv8 OK8/IRNȄZ9GSc¹FŖ:6!Q*sE3V/l0I[l?t^+PWrqQ> x#ZDp2y3H=+Mqe.]ʩ.q|wy" PLc$LR!D#C=Aے:>UɶmKY-bW"/fZ]$ ICowl9)W J]AnYs[gͷZm %r1#Ѐns3a"{nS(]gZ]\K9}+P+JF-/f%`)r+ȵ}[(ffOr y#9lA;w0ĸu-mߠſbU 2yɲ|)2b9IPyqp(2\I# 0 * d@VU6 طxYiNZW<+ X@VPEl iܴq=%K 67wlQ2!6!ѵ4x6$gI6sP&q 폑 )xzPQѳUm*Ս9,!֛`OQ<)R7%xrMy3g 9.G %+&3nUL'0ElkoNA! b9ӊ$d)V_/&3 SZu%(I)dǺ2AҭiT q1OE$p[  86[I;J $Ͳ;5 jס=48ty6akA%(gX GJt̴"@1z? {mj92·oZF=69Xr1ԐyEfP:.B 9Pd'[P wExȩI o+o %uft5Et,Xu(.a$ @Cr.琇Yq6;؎z;Db'4Юw߈kMOCljol$yqRm\OE qCUXJ@hAThE{c6lɁ]%𣷱)Qw1I '7L/ YUnô: &IJ a"o+518r)ǽEL\{t#io\"R(rJfNPG\QXR[X|B+x؂Z!Qo/h$X)cpTRz<[HtN%džJ8N晜KH?߆bV)J'+݂K=HU[Q\lR}on K#M4lg$/pX`ߒKcԬpd-*^0/0-oNuz G3t92(/.* j뒂M6ͮ`,8dZιNĘȷ?Μ&Wm^L+(9ry`z.&ChٻUT䄏1z:}6FfWSuQ|~eudjo~\2)xcvh8 rb2qcy8$qh#zZSLgt0Px5U`;_}*DUU~aQ#J_)H!ŠPh A_;p.g_PGE9H鋤&Hr=I4H2 +?1B57:,2"|'ErGH%|&2 cH2ImpUP$t蜇aSt [G_Fph8?A(z9##Lقd81(dG׮dM &TЯ܄޼` U[Ki RPkZbYçVտ,#Kܔ%Zv㤉ybVqѺ6?kj,n[!i*̓|bA魇$;ɍI$$Dجn>NPgQ%׾Eѥ @(%O55qw"( P+!f^R3%Z})Z~瀗#HkT٤DF٧eyPy+[ I'uG׏D%J<'|NHzԐ$ hJ ~<2=K>ph*@H'EYVHrL bRӓ%HO<9K BOѥw+=ϕ"d&ȔM62x4dѻ*H9}If۱"* T ܗE'(`.vtfMisxI hB~{>PNx,,~ 5"c%>yx-; Ufjk_ȇoUu>VݯY]ÿcfeͪpLh Ǿ+˛${ܣ8},+F"g$Oߛ?}%OD+oKUgkM?t>\L ԀvAqʰ>`~`. 8!Z!p0N%?AY^[F #fz=&}ܨ^)#-BVw}Cߦ PŔ:f<'u*_'eVp"C\YjÞF:DTH]5oeY$d=XW]Xb7E,b|*K]TgډfE5>{],3:dL$_=^cHI'O!}Ӧ5u 4$}ZpSqHl%OL!WK K{93 +fv;<[5mȮ/1ʦB)c Q<$>ӏGç|th3>^@6+E4 l4w aFAdHn$ CW2G䊣# uٰ֣nH_b\l>y,x6r 1hPSrϳYl{T~h>y DlNcY͖zk\\mk)t8&o>W$2Țcx!*6f/ӽjmo6HBK8O^]Jk/V_'T3H4tlؓ$[!i+Y>Hm6L%͋3#;(qVRIc{/i6c!iTd$QK[-ImY|eHKۂE>&//k,={7°Ё (VR1K$tKd%3y%&X^zi2ֈ5DQKC؝$/gF dWBT"2ok䃗1eCKdyie(/yUkjb. đl[Ґ} d*BGc*DAQgyJ(+䖧8nRIdkuGOgH?r$&Dɢg&SH3ʝOS+,KZRcOW?J'a5>p^ä́6/=#"a51݊JdK7P҄ |+13 ԓunsȣiNՑQǥMU4)9@^Ίp`wy) F)Rw4ȚI)Sp2K$uuM %Z8C_uƨ2rscdy:RCg'4K~]0I>f,?\*T('Qn23LMa U:?UVJ8=eU7" #F~ޡ&r5EJlƣd#8/N4qbrz1^d7COK2RQ!,"Sԛ_Aɛ+&ա5*tFJYʴ 0}"6O ?!c#N"_>[?+oLEVbMg$MJdPHBbde-rdNuL#:jA84JI8=,/%j.orxu2pj* ̆7䫓/V4H< 6Do>!(yF0<~Ra0uD:tP ns1VKQڭ7  Nor ٣+c\bpt d%/ ^,cmtDvGA2 -Oe:,TB(ULQo"婒y!XD1HA{٘kHoMhͽ>n{s2[>Ulb\2 ( :"H(>73'QAΚ(NN?QaEAG:8\X$ nK7H_8T^2$FvxQ4E7FX 2jؖ5$@ k,}"jOBur$4.[ ށ-̿xe #LF9:"QqdQ:s9hN+i-+3++1 xk0"5t+PE%nޘ OZWVdד,DTl4%NE7qEs)&?aʪ=L{W46Л5ͧhFU׷ꗲ%nEdTH}s猼q5zGuyڢr[mTU opEzk\7mGBͰpyv(?\~B8,nD遧[s)s߄F fs;?/T]-7sy$ջ.Fc6ti? 熯g}= G ܃Q5{r,ke[eВ5I!EE*OuO.6c/"_LuϚء܊.]_E/7VZPXA%,|\ oCR(\uJ<>}<$vTՕxR,LjVԕJqHM>/r%˻N"k"uү@SP)Atx ~Dyd ~!,X}cCir'*FA4i5/byoWۂJyvqsK3t31 :|:1y˪!1 go%~~4 b30/P2B{Ɂ+'VqB4fN¢G4N?yAB1!muԡ4ZXW=I^a= Kvw)̘j5$Rw-.Dؠ9 HΤH=L@0$,XT3M?>?HtxijAkc+aObEy {N|EIRt  #LzK0{&]P ab a'5O I3&mhZd(.,]vyuG !y_4, 6PI3qՊ>ߏ⪊) tqϩ^=oo4VJXCqT@C>'B x2nгD1))Ab5d(QY9Xp[i-K`"QZ1A;n?!@h!"N=HesaSL"+?gsvʂN(tU Y4|9~OnYQ fD$`1\v7]$iWT?I-jKb6=ԓ2"E!1xZv͊ʔ&E?A_y+Ȼ2KE,7弱9"I*k:3I*[J3HTPQCgjaa6wJP녌*))`L&FSB޿4"Ձu9xbPMAvpZK/`j1Խ3'G{ZD+.> 3ÍGg/VδXK.YLEIjrXGgf\>,9pBrY;MfU`S𱀬2f8q<ʍQT?b*'̐p\bKm*X(*#eprpe/Xx-"eT%}q+:l8zUaQ1|@?bﲳD >Wλ(p]pDEk]v8 <^ޚW(0 psKlBR#sY^-Up v{0i5᪩` [KX55TP|bjR!N8f8Aax%G*b}E BQ5GdxL`3kTV4cZ8 ^]Q >,J7QInՇ p_y*b8 g` %;_}[ի6ʘrBe9*j %Irh OSx`ce͆m’4p)A*yO#sJU:"Sq9MnvwSUc`+y0;լ8F]jwtNW.S /:lj-M6-GMF =kX a-oزn745F0Z蜱dof+beBڈm&'Ѭ(ISѤ$m],ex:4B1s uH7I}>V[bm5Ɇo4sJ124#|.h](RNfO ]X|Z ^=7gF9Ń(j@TzCBIC@t-kvX/i^tsa%-PUҤt|} y:OspǠV[TOVQt9͆PyAL*V` ^R +G g@S1| A?£7Ā&'GWg䥏2b.A^K]R7Px_>"x z+iBH(ML2s*c4o`}1OfPb} 注VE]Ah 8R2oy^o#\AF -KD/+K" 2vlN }o=;$9IOPNh}&Y&?Arab!QF8'-hИJ0NS ʉ .ZcEr$F)^e'> h`$?McB W$ou;Q-Tgkʐ Dwa=D8B3>?H4YwE%BtIa E@u~G]H}C۳s՛]S/fgni[m%ę#{?pr՚/Д8=PVƉa>ƙãQߎvt:y:L*)KUEo~ܴUrJ_ zA¸^FH,b;83vӄ|0ٷl\B1 3#Gt!05甎--L} 9} gĵ<[ivȻnW_ fؓx4~Gf!@''EY=9ÞhyXl 4fo :RauQ,΃H ߨʼnP7].3aw.I,:ݞ>:+ XA;mv~թXŵAHFOd̪#-dȑIe3k=dYc PE s1"w-J_62S\H\ٕL SU-?S?W>@WXpP  1d4]^7ʰY6S`QkWv PvS~rlESdϱΫP::Z[ĵfw[i;,+K, [ZMA6Q[eCrzr9T R#qb )`|&[$//moJ^~7ω,/Kj;TCa\[C[A17=q)Z Z0QX_f>x}!먴Xll\p[&H`&3l\6UkB:~KF5X=[DISydT:*oꔫ`(EJ~/G1 yRSVK^Ibz2Csv5%eS=C[E K<ݰ*E bپR ~Tz΄~΁=JOgWv{P:ڍ+&+'X5Kƒ4M֪t'Mt p!vJ( H\ym~gE8c%bqvb1r!ǥX@:-X5`@6|߄͸.+zCG+CqpJ$ (E<\坨UfʠjTPqʑaM Aը4<]R$ 'ӆZA/0@[AzXTSYkrܚȷ 1L0 T| q5ʇKz(>_TuQ=QPiTaEd v!Z9/0f ʭ;Ip5wZ)8WD VѠo~M2q4dؙwr$َ.ǿ O~y oo2v*~d]\\f憥9XV*=N;KIh)KVHޅ 8k*PU(RevV*+\Cvڻ7L/TCkgmػnյux]e)ϖT~={]>w8V,˪aDJK\RSBx D`Ўf\#&6Åpֈs2nAê̐ps%5LO<_ 3 z~2On M.Hf3xCH> OMct4qgP#$"S F|I&ʵ5d:$7(ؙ O({&ܒ ,权:!$FBx <9r/(Se鈓|kF/'M֣!TE`¢JDTW5A֮b 0 yO6q5 'UF9q b4h#G v*5t` k)2+ E,Kד 0:9_mu1% khh3ȋ=x4P:YDQ(ѐb[Hw/~:,Yg nH:sՠpvA>P3ҿO wMEՌt=CxqlvϲpLfyD ٙ لkx͛,W=3!K(]rk(aa~9ΉV"썔-'X=CȆߡr@uTM8Yx&لg/&Toȝ[yr)GK`$y݆smxșmxe:?߿/d,~{bUyr \2rb'~SW<)=86|59`oP!h++$·é{isB(_WeLOpr߶`@oFlpnQ@Bވ{t%dO܌$?9j7Y%f$ܫm N%xGXӔ_:^DsҸ:w8u#}z92x7H+î&^}W'h,@TT q"90 sp߹aҋɈ =+q,ۊ=⼙ [8pTJ'-7wK6-x$^pAR.{$3OTVc`9ox̅ ˌފEIh7ȗ53kKdD"9 , ~k5t~uA3G崟W<nV&Wm,/XL8]S`L1T5$Zhe{ϡd*6gTe \pz8ΆQg1Fz^Hz.д9J Q%0ڙ*^áOD鯩j/sYTjz :^^Ķa<.C58=+w06LH?eOuu=/Dbr g +ֱV;(x~_{;/Q 2 [T͋R߀`q|e|z6HNa%$iq*b4QHQuJiRwo_m׽jh.5q"ބ"FP^6GE'éqx2zME!.rɐrz͇֪)z(qQ-yRF,YN@RTC|#e:{"''0ktJ;U=1R~e.o_pBe}U3^^b dDTòxxd)BDϕgْDOx ?hʥj'P )<|{AEiEZH_9r9_*]^{½ DBFd70x\Xlv&N+4Pc$Z(Bo ? ,:mq0j[4VH N`0Bڅ (ؖ<5Yp m!shdp$U]oLܑfSW<)&(hieШ2ø9B2"϶ xb=T5 P2[x]2wPkZȑ9»x!38<.-(xPv6/&!H=_(r!45 챪HXKHPҠ{.7W]Ks83/f_ʺ V)ہa#+OV[\\(Z a"_p0ws;3kEpϕH_.jBhCJ &U*֛ۭ36 "x x6 s!E܂(TNt%[u ,]|ɐKȖ~%,7jHs`\ On[Hȫ + ;gU.Z&mY=vAa*ViXٚ2FTiejf) VS)>Y8hwqYdL֡S !16],ωiP\MaGB|cd 8y1JiuZC8a-C$@H䣘? 4%6^BmԂ$Ф`gY>8`LESGbȂgkY%<[ Omb8"9U&Meh,4nS&y m1c V|ROp\78X-PQS9T8w0./W"9OK4S(3\ޫ%-@إԔ5ܺJMk݂oDe>*vge 9}2gs"~(3EETBq4\hVgk`OlmB 7OŢO p4.r2+a\.#$L?!Z`-gFhY*d`QSmBn6w&}_ЮRJJW˄dzbZ&GE q4Z&5oK *@IRXF.xl!$ȷj4~e 8UB)Br4~~)ʆY\Y)4DX̻ bD(t,H9/KtNZ]AiB9.wV Gi8[mĤdpۥ| rxD\RG¼Vҏ_BHO49+ZݱB!JUHܑ%D&JJGWO&)uFDoK2 ȹ.<;c5xc`p#G~CG.:&5#|wo9bsʗJN^$iM&3vuIy[&M̚eeLRº|?}"Cz#)'EssߠuG׉ܛM"2)y'%2<#%>^{frxzz {Į\h u[PIZ,i2_8TVAט;2"2W#{%^8%e>=tn8y{q^޹CZYiXxT]LU=Xav˦[b-JkD4(!Nw)2K$I)J5j4Q(cCXcbL 5 56֙R3g|߹gWbKjQ^d {1_2"Y "r~"3u$!s&+V 9θ%5OApZ-aUu9c,zG 93LiY!Qt,T_xW4o}P#fJq%v".f.5>Qԡ&#pYoCjV ˵,O=[ fh|=W->s.rF\/9s3JksԨ_L_iV+lf,[@=M~%F3Zd.j!P.|ORO5BHPlIw 33a>9tɮlT9><NmӴ0wŸP߅ȲvVƾP@mP[M{V|M*R;0\lRJRj| ǩq$Eƨ$Xi x,PG%V-*$i9ѳat;?{ʐ7Dܯ>9̬hK  J[#r,Ph\VX'^g{PcZng>tJ$ӑdkbDR{kf2?57#֦IW/G4ߧv—Qx zC뎃nu>v Ȥvkts&h^xGJWЖ9<l{'ԋ^#v8u9X߳Wx[~ 'ױM/=9oC~I ;Q@ (;]0- SRss'KOKChOVE'  *I\wc6GtOA'KUOҜ Y'f#٤8Ud^kIʹĒ%.NNļԢ|PR*P:HeOo7Y6gRF&N Eɩ)@iR̜jĜr`BJbIBN~q朼d%6'tpUn+xti r680}x]HSay6=0IcVs8G)5ۚcԃm8N=(>n8{U1|/M]y!DX^Mbt tv,zóqR].'/Vp[Ntx270(^gR /'3r,?P.L(2H@׍ш6-K$mHoG P=52׳_ %vp{m)i"VZ< 9mkvs6t8Fl*&gp37@tqWc qWY帱t@?Ho ۻ;z;&K{f0YEiE񈰗|\w SXb}`%"Ԑ'>'h" #?X.X`ȗxhLj`7SCeEH,xg=WAj/6(I$f3 >hz15.8=@!hsԎ@vNj!Faph°b~t7&u1 JCٛSv# Q7Za\xaɌ,s4x,t"fvz'-Nqpx{vidF9S#} }CC##+Cs+#3̼t6;3Y1N`k,{=C|B]]<Ks23K rJ2rR7kUrN3C1 >5(,''M '/⿶NPhr\Ҋd'$rm(_ȸr>Ʌ)g'=vf+]Ɍ$6usf,J-N-*KM1,kžy]sɏb`VƓ3c&;[Iri&&ǧUN扛0Mk4/c6ϥ9f[çVZ49Fjo$þĒTM"Z5ꅶy$+(70vׅAci´&ąjcdY$_zF#!BKv*|*N@,#)yq lª,ъk- Fe!WkczdTlŞ𭾔x+~YXY85<qrGfFWmwqKT ,"81N73W=x׀3.Դ̼TGx`o\y'G0ьYCDۅ1n`ĐqH3̲)% Θ^(:}M,N>Tq;X2 lKag;_Kxme:´a m^h\=Nh3k,pp)dq$0x%YfC Mxos-Swߘ&'he'*e"O'_U<%D]̼8X7lvx340031Q,(*-)+.HMfШOb#/8x,PjB,Y߳> x;zu#d{FY^ҼTCC#͹In:x[CR?$KUz,ѓ}:8t#c100644 version.mk[ޔl( r/* xx jFQ3#}3}CC+C+ '02LOenhd ܩx340031Q,(*-)+.HMf8»ωSˬ-MSZqbx,\rB,K?3x;zu#d{FY^ҼTC#͹Ilcx[ P-ۑT Ir_ kٓ}:;++ZhNPT{MY100644 version.mk4:pn E[=US'w2xx jFQS#}S}#cS+ 2# BYJȨ%.y#Qt7$5@g /1%ϑ=L"'%ASpUIWgf :{h a"sSS"pP%3`[w񝳮*~9t;¤$Kg?3s7UɆ w`(,k5a{V.4#Ԕ$>׷uP?_<CY2gf>$[qK..HMf(47$4I꫹ ޯgRZTpz'sk&kgmL3x뱜d<›g}aD]3x QHD L7w0rqi($$($pMwq r'ǮNes0<pExcrxC^“3DS34ғ 589A"%Eɹ hD '9ɕbF0nxz-lxkY̽QLDTHh$F ɇy24ғ '11N6e f,ޛװD0`=xx! Ռ& F& V&@dyQѺ?0rom#%3Daړo.3 (1">5hAA:O2Oh>]Q((O4z{2cYFz/T a4d~/kT 1F0zM(?}u4yeSrRB3Tbt899sg4^X81mhF鯣 >%.pc|x;tiF!c#}S}##C+#3rM~h/yfLnIytx,s@fC -34ғ 589A"%Eɹ hX%]ݴT%M[[MҬ (9@2Hn< ܇'>%vx340031Q,(*-)+.HMfx?}o#-sfK~d=Fx,{HdB(,q BZTx;zu-#d{FEF͉@iIx:uQFuoZʷ^]Y1aM xu> Ռ F& VVF'0̜9b@ћ->4 xB+Qouo.EkܜfOD[ LÈ.ts^Ftg3Ѻ(/ML\M|bD)aL&_ kbhv6mNlQG|.ڜeVѽ^a_gl0?ݿ^ Γ R2&LWa~>wz(?}Gt Ƃr&1 x:usrAx_uFfҗ+7LXo֟[GNqUkg/{ MM2 22KN{ ZdXw,_d&<Q%G[yy7Pd0= ڳ M%L]x֭gumԵpγ&BqAj2##Ww[,4YLj`x{o ͑@̴=RY*x;`hC>)ZXne)ZXn_PYi=ِ !zx[ Ռ F&@dd`ddejled8y Lj|\F yœ3KIbfLJ 62GN5em%Ʌ6s-.N: ۃCyzx{u Ռ F&@dd`hfebiedyM3..N΢ԒҢ)ZXn8ss-& x340031Q,(*-)+.HMfiYPw:x,{NxB([|&[ɽ̖C-'1O>ZPZ79(!49E{r$3/V9y!DRǂ"#4-7r5qxsvT q#T+>% |b ;1yQ+$^gw:}:I$btf{O 100644 version.mk!?N DCPrK1 x340031Q,(J)-`hvGOQst9?ҢϓJJ RKR_y'Rӕ֡_{>.Px;woFNt{5YRKҊ'f`T,4qrKfqf-ox340031Q,(*-)+.HMf]sgw;t:,'Gx;'-4!bc&&fX&cV2+C<,7H 1x;zu-#d{FY>ǂ# #͹I Bx:us /y?o[e9z'>eT?ְu[Խ?]D!('3I/a5 O,5uMk_H.'XhԫzOv9PeEřyz <>~xzBDvx[o r͆@̴03) !8x{uj Ub& F&@dh`ddDF'0JL>zJ{Zb6G6`Do>o Sy^p>"yI'/ޓƙϾ>y##XOCj#TFuslFy`͎9 Ӵ6じ##]yu*M UM0=6[=)hFeGyvMg6kF8 jJ\\ 0_l>/U&G[INfTR+͍O,|jk|]4d;MδT O0␻-h6]Qp #7 +D]#=l}%ƹ">>YxBѠ\xtiFQC#}2S022422ݼ&L Z f @`h0Kds;wXC8x340031Q,(*-)+.HMf8Sm5kMJ,2cx;'|I`B(ưMLœe'0M`Қ{&Wld^̼p5,7f1gx;zu-#d{FY>ǂC3#͹I6xrvJoI+^+t]ޟku㢫/Qa[:-MǁuF-{(y100644 version.mk=8(U=m~S0yMNы5x340031Q,(J)-`4'O-8&lUjTp%y% s:?v~d}vdaY#kJJ RKRw. +C=ܲ+)'x;wFFGU6K11nƼwM?lxȷoCYי"Yx[wwCV˘4DYxa߆0<ǂC#͹Iq ix:usɶOը6&)g.n`040031Q,(JKL`8}14cGw,մY=Yqm?A-+|*蹲p-hc.*gf3O9%::_Nj>H%xR_L[u\؋ Gi;0Ae6tqYs{{ w^v-ZYÆ1 K%t;'5߽D㽌`|};_|pvtzH)ŷ=eD*Ia&4 ҏ(yE᧓(J6nm{Zq/$ŜLlj>u̓p#yWZ]K#,au-xZ?#A-RmkD ONZ^'dw|&kʌ+G?ۈ^hĭ 3XJSU8&t;ŕn8Q@px,hd*ɈJ TPZ^v$(2}<_c GB{3>hR:,|H!FHRJ4"QB^I~&Ѷ~ʫx?!h/4{a]?<[Hf|.?p8pci7t #$3 p嗹dpE8@SGY.L1Wg7NꎚIMʍQMYDTiK'yuɗ)< /etvvc"$޲6~kNYy%GGwHDL^ z'N~q<N#v .}paR%Rwz=_WnK&}ţn8Hw0mbi\cƝsLi3}0 ptnq',2{#cv\^%ƣEfCub&x}AHSqS`Zm6lmk1:ۛ=DJ(2EXKBz,Ku9ӅYhfHm *|*%t]d 9 e)R6Qq7O1Ό >g-{gXq1]+Ub%vclBcSZ'j₪hlHt'3xH|H GxV͍BC}EZi޹sKFAqIѴ:.sDOR.m,edÅLFջ5-䲇 í EqCaԷ}ԋ>r>:7>rxVSLx"!j(6jE6v%ݵu +kazՀqjjŗ\No'yϰ!ⓓ=&p?2Ke d'zr?07Vƅ|AܮxReX2~Ͱ6<z`uE:q l܎~\s(Tq( 4x+r7hEnable{41.w}a,"0h( Nx* Ռ F& d`dheh`edy1&S4lm 44&5,io.N4 wgBMFqj^qj|JbIZ RV}va0x340031Q,(*-)+.HMf}E̋K _h2닧Lh,x$0gB(0Nsط0E_x;zu-#d{FY^ǂ3#͹I)x340031Qtv vehW}~u!DobvjZfN*r͞iL]x'3ҡj\]|]gF18'ė\ңB{S4 P,Ka݄D-Wފ`? 5"H/8!~N#y7N?Pg1♰ueIS4bֵV e ]'Ty+i?/Kc(`8}14cGw,մYP2ʾ_":>OZ\M0 )-:Kx-iާxa9y/dbA5*p0O=Tdnj RdCqF/Usr2j\~kRz?ϴ/+t>`X9C_7oBK LjJ RKRW}}(KV̟/Ƃ,a^Pع5g6[hW ]>yLjv+sԔԢovLW2xΞ.rWFwTL7E ?-x[wkCV˘_Vd(srq$hh*Ts)A*LN~b  b4QlB ]x}TKoUi+*D[%6&S`ZR[~PXמ; 㹣;3Nx1 lذAݰaqflgR{sfɯ|t(3,MMP 6לS"h܅1APَ`mס ʴA@ATGPѳks I1 ց>PӦ@r?dTǔ66đ8E TC~z˄Q.4qpf SŪ̀_ґ2À6צkVrclrxZM9l9fiذg Q 3@n;r@8ܭסTU(m+56kJ}wNhaOg qtfء#\=*Oq  yB nvXL;2 áuiAgfg]wޅaQۭ>fV;v rr@xHqyF6/lnCϙyO"g/iȕ_f5UږqF,y4?ۨ>TzqM${?J_]<^4;6$Xu9rfQ ~\ wk_Ԙc961>D'qt/[Z}t{-" ( qgwlq&[:2\ϢonIvuBgkz"qimsDŀ+Lwb|H3)qv:&ӡu۹K%;s/nl+%p ԉʌLL$fcV"7")n[]x) jX No '&}+?#ˣOGKS% "x(dzb\ļT܂ļ̼t.-ĒԢĜbĢT /,13'1)'U4/%(X!? sS(MLVLN+NU(33P(LP[Z tKJfqIQfRiIf~Ȑb F&F&@̖b H>agK,J6H .Jzx;`bQRDXȴ2026x^d{x$0gB(0Nsط0<Jx;zu-d;F9>Ģ #K#͹Iljx:uBd75%bf9\HSQoA5]f&& Eyiz Ѻ^WZrEkBBŽJ1;<^W:-Zc[_"d&%3\ңɋ"BUKϜwjLX0Zֲ/&eY:MfPpuR3rTT&7u_cG72ϙ>n@x340031Q,(J)-`x(“~-* EEW&(.QBY\od F Nd|%!( qOi? %v *lF6-9ֹrWV0vzf;tg169:)-LJ7.ksr_;j^ ߃kJ / ?Vr+ A艚+YVbjmOu4c F&tͤ=75ٝ/寧 \׳%< }#p6t79TjyT. 0w~ns (R)+~uf7J܍npB&H#-m6x;zu-#d{FY>Ģ ##͹Im̠)x340031Qtv vehW}~u!DobvjZfN*-nZjRQ\mn WG_W nWc8)22KR6;KT0RDGJP#2 r,w:T1r>ё1%yiz ?J^:6%?\ȏM<1%3h_pjQܭ:R5Gd e k%Z4KVl4pnhy-HR@F=Y(!yŭ4β*mARS[tV]a_A3md2|MQ2σ 𨭸diEP)H8& <\pj͗ɏU{aRNfP_O1hʟ'&A0nĎ%gӯyK@)-HI,I:q{ӣE6|y(CY2_ٶ]dx ]6:6ź Zo)eEřyz S+.u|^ekB.  Vx!_l2;ԊɏL'O`REX $jYP[řXh,G9rt@Z@8&3MPPV wv qww +.IɁ4YST S>Tz>#^qQ^QA{|=d !x/\liT̂tbԊXr'OdT۬(VgTfLa2kLWMbڤS\YV2Jl*D.Լ9ٛ};2ͷY@/A7q0nvbDZx[.6_lî̼tbԊ&cRl(RbT'g4LWl^Rx//ysC}㷢W~{Hi6+,a5YY\Q3 %$aVG7Xuv˳-dVxk̭__PYQᬩ`d`` .*/J,ϛΨ lĸkgFM΢RL,#]x̽IYq"s] *y%72zP+x[_YQA?)3O8KY9(3=DAYSD(\T_X7Q,3وqW *E%iśn@`x{}IYq"s *y%57;1gfixC_YQA?)3O8KY9(3=DAYSD(\T_X7Q,3و_ *E%iś1n6cd!$exIYq"s *y%5713vi8xddYI _x-MhQ93AGEJ0 P\Ɵ A)"Ql1$?hlEAQY<`faWE)";7E8={/̹xyhwn؋z`0:gWLuG fw=f 2+Is䄳iJe΄˼(l~3RkST?F+ܘ٢~\,+O~c_I ruSyhtz莍$݋P&Urtn=i\W[DmW*)%'"Fշtz-7R Zޣ*ҳP2b6|dֺA?nGx;hH̵vme+qT5&@!rގk::ũDXT Цz@@ZB%kNu_Nzqx%\IBM{|S#512#CHx[OTl9|v>YOъe@L_)X~Z`SJYr5qH@ju/8J5xr,6lqYg0h*6=^B?F_أ)GWGݭxLy;c::q֣Q8kg 6[ݷQNG!Qks 1H>85lΙIbqcJ«7#ڼ; K=fArf?=WIoe73qA:p<$zַWp Ç@)9%d\Ȟrs r,:1͚խ{Wܴinr2GB6mђΝ*aK&Oou[[}Sā'M}'_i>Zߘ !$xYdE)~^DA.(v`"dղBQ0,8# TZ͢F>bmi)ƀ/.ǝcf<]E>I6L&_ݼ4) * rn`!5EЂWF) ǧp=v>dȷfX\LjQn;.G>WҸWK%gZ&5?7 aR0 VJcRz s2>˧)k;}v?V*]D%A.7,210zO3Q<ĨINdJޏ' JE3nPAqd-/^_WϿ-ʱ5ת[}1^VɋZ;R`_+k̠uI K j/7^0YȄIe^*V!,&iwmd= 3b!19V}l -+2Ac+~(VXN+aNC/>ޤx{ti®av F&FF &VfVW2Z aiLx{qmc,3L0px}ϊPƩ t[6m: LөImr2斛" \,yąQ) Y?/_|z(%Zh/SMRf-4QbQ@S"K2i 4*r*0^Pi9OqNicRsc$-%A(ٖb0R}{~@i y* кmc dQ3dO3Z9愪b\47 o\l2ŷ(XZf,r]pwh60 1 a6y&`r@HVYߧ\9A&d{fnX\:X-7Т"WMmC(q֒Luq#Ds"Lrsae ݮTV^zi/]LCo:vZ/;ݮy^Kۿp}\x[-X`Vb F&FF@9 [x340031Q,(*-)+.HMfwϩj ߛpl:˼J =KxcXj`1q+UTd=x;zy2;g@P|c-jX`h`d``29їOWEI杌b9&x:us=ff \aa 9 Oe ]xVҡ< )> nZ <!л? ߻`NO[#66sn)7x_;H-V䏕Ln!O:100644 iprconfig.h]7f[L`BK"(͊*(T"g I,Sxo|-ݥ{?ʨJѕ`AQ'mc e#R1qYC-`dv ޞq; &nM|h3}FS~2ޘ6ytL< Z n y) % 99 )E% I ey)E) j ťIŕ% ) Z&0m? M45`LoLge6nW5=:Id:[-S5"9]nm\xk9t eC-Q# xi®ab F& FV@dy%kk: pws rURPHHKOU(G|^esBujrBIFfBRf=PI/hei#I,RR+.IMLlvds"T&/,qm<I rx:u\:GJ^^ 쮿f&& Eyiz Zk9UqusRw=ӹ;-?z2F/90MChLZ ,'dC}u`F52Fpy4nionھa,&5OxVmlUιkvV}Swέ񭎒 B( w}Ǫ]oVVJ&Fd#5(ā8cA`@5G><9?;'{%BPP=Ŗ6ZjOkjX+)]+Z˫Š7 I6-`U\Lݞ7[W664?l5C笫ga%UC8Xh4MZ:t@CӰpMMb&c)| ىFX),C!`!vVt0'塆wo^%@rO01MĄ{H>Of7PgG1 Z6Y^kz-C8GdXNLF> y||fmRKsS#RkKCDŽ䂽ֶ\T?0YSԈo\ Fmk%v̱Q{x1G׶cSpzvc .W_Q|+w2-A]%K=0?ox8?q!q/Pi.w1#C%&V\+X>?exK]m#%$H_'zA.XD Ǡ7H.yq`*VD7ݰ*#<^MRz@PoTdI i`$䑱g *L_1(35+Єo_ݫSk Qx8'yr-5)M|)36|s2VZ|:NSO¬$ܕaJ??S:M<8 Sq|%TI~wʝaYAב |zHf\dqbPe7gbjՊ [:heFMa(i5N0`cX8SKTZ8;̂kUQ} yA)t@>_pU 8S`i -l(Am[6<tLȺDU;+ZG Ky?lpd,g']ICE{v)ڤF<:&Q P& nk}/ߔlF5[y 1a6(^c&KC,HJ_ΰVZ;mwČ٨ 66ͪ#~u=IKBbrmi LI AY*"ZH9茊¶Fi.EYBTSк~87F0&pޅbt*İWd+5xIaTM|ݧH$RH(L-vK֬x@OaԌ)x']Y6P] " "0P Ck:T0DsxrWZbiNI|qj^銛/0NIW\T-1Y5Un2'RX'Wdz@UK 0`4̒Ppst$6Cb??Mx[һL vEJ8&/Xjx̀_ hru100644 iprlib.h}-ЍvMeޞk,Exy|T89f’}eI2!! YX CfIf̄UPQߺ xV@vh[kn낻jj[s}I?~_?{ysg鎄I()Q"J"DW}eX"6 M0ڌmFCRdeǮMZag߮ĝQWoFfڍ.͆ UL};9 йT[\f4!}tːWl16 7A"ղťNIqelMsh4Vqjbh5dbPvUtXFXM5o8{*cd,#FB]nbGmƾ! AKkuwŵ]u}B?%`!M{g՛;i'8UMJ[ӾKZ:;ťmbռClhol*N#!&vJOѮ7 <_OFQ2mF:F Q/ZFvhz 9;KK M}b׈ۭ&+vKsqֈrX3W2Rb!}/QJYlvVrU(j+u:]J[+ݝ ZRo3Ɨ"Dv`2#&|]KfƤƚF=z;ETUBsNYoX.؍  ƙ;*O&iDiCVN[Eo&T i7bFm̤3+"_8ET(vhJ Y=WEU_28S^D\A-r ʽ#;Qo߶I]Hj6y<57N4b5}Fiy(Vz]LC|Щl4>H4-:mC m/JIMA JEEHR*;퉻m: F ehd:d}PT7ptN\_(/Tō]-mz4wPԔl VmqcK-:>-^X}![=?}^TDJ^n 1Sv4P3@5,g~R?C ?Q2B9ު)b1a$0#ޠc&vQb-V~ӈQnU7VF_g\g_j{C^>լr5_5w@\d6o`nQP4,W|P.MV+uQMm!AMQ"+I|uE<1kFe0G*5YU-N\ [ T0筀C=A!.+(3(3՛.`lj^Bunrjrn D_&jkյIx霓ьӨ2$o."I;?ϘtdFCָ_hri%'p癷9״/*r/T,Jʦ=V51/]K,#w7J_֑mтZOF4Q9ז,EsZPT$ѵ;F:J#wWgw+2*rӺ|.%{ڍ.dh.uĺOY? R VSQ-?2~~zc{ 3&cKa&RrW|hyϋk/ʞP5gETTY*]J̢%;z5q9K=6#EytX괤zz9@$tr'ud͆.uT"8?ӛ O"[rrrH/QI%8D=wK>!2,GOXb,-u_(/-wq!9ee2apj<1&K-mRFp4FZݺUn^KA2xHCH },@6! z؁j{Ss {GiQ`F F,h?uLƙ gGr; ~ d(4,נdِOR! clj.edq/2,< *̨Cfcpzƾ X "+1oڧaIzFDqVePsbQ_ Vj4Xz(ϣpn1#P ex/H :o@; yv 3J㟤 z:ě2JmdInW~ɜ¤\BB1yђ˔4"Uk9b y.E^z)+58/Zb( bwT%ʽA3 Fj*+{cuPsϰDY9sp4/1nnOPCC^WiP]!լ+ ב}ǸP!)LMT ȑܲ$Ql3O=-zW0 F˫X\ؔt$,f_D\Yˆl,BQH;b8LQjluVl1[ F# y-p/\h$2BgJ9tIOG冁rJ8sMup;.t/4'2J(u֝圵$,1MH^9H2fv,%a:uk%9t >gJn(@N4QMBXZ* $lRGXL,6B-&]%3m%D:Raԍ怮BRws4,"?hah|HfhԡdpT\\Ct2ZreT9`6URNSnA /42#@OEVdO)+dH'C!dgOr$Q%{ !IhҤk1XWk}lr˸3*wK;WwG|Nx0p뢁z# zI!T gG%Ņ>.9.CFzvuZ#!6AB(+TRԎwk^WrQw񿏺b$o`Dm21Qd﹵q1X'=-!{oK!n#c%?{;tm?`Xi?%i/oiVIJr)Pac2LbFVe RjF5 (~ j=weCj6j $[->w y"`LѝNKidP>5t=fК"WA.t*rl2XMvR~=J ($;=5Iz POBX o“ 13TϺo5n[ڇOLbtuOdz ؤ"c_"~YDX5/cbU]MIz2(Sj·Itί}Gj$'%dkV@K?Kߦr!*KJ?S$y"bwGh7id?TJSgL HC rCh&XX$>T]]MfO g$3Of!g [~KN1h%7'? ߛZ?ITG%:J_=0,3{A^QT ,zm}OS3ߚ[/s|!T^ݱz"yt?1o;iHʼn\ְEtZZd.Yd]Vb' G*yX/V UbX / bX-yz< ;i5&Pr;`?[ʕAߎ+_kOBl67`yع5 㗡9)'g՛ rP"+s>N2t$$?s3Qpw%V4wOf>I(LkZOTO 4U*|/?Rϧ Jn/ՑkgE2ŷgD87G:;?JDWWE;N_x s+@p+/܅ # mdsk=9B8"_*M҄y=sɂO>NH!;B䉝(*$ȷ;cBܵey-a{LCC<'E;2iwrV˨;FvidJ>)\ A@7O@)Vs/7 <6˳thFGz6PJa:r4 ϊJka ?%;S 4\W:t7JK)F,>kO1ANP(qMϳ%"b|%[[S wYu8zi9Cː{SJX/״@TSZ뤩 @ZؼE3M(nr.=Klf*.@_åՍt-[oMY $ވV2],rbUuz2T]T Jq -p*g/Q mK;VaE)j\Ĩdpܑ.} 1bh OlV,$~fP6m">HA~8[8,x7ߵ컪%xfvs.BQo ŧٮAɕ{L|j P"Ch|3;u#K?f c(z?\xqV:%7ў G'[RKn%CI-7/J0-"X} / J \"|j@F/Y:WsiB9.\s x BY8L\ƙJ ~Q: ~jg7X?t#8;?Zmki Ur2ؿBEm [pI p< eP,4<h: h/a:k2,KVO5-Fi3 ..dpS2CDY0EpS(u8xLOd7HwLȈ@q(+ZI p,݉t f6%c%`f΃CILt+u.N6(ğI842! 4.{b?@rBIiҺ5Q]Z0fZwm*JQ-Ä7RQ}OκJC6vHC?ԲuH:*d)%MKGiQ%tTȖq@\h,xLD)ovfpb 9,U']V+vSш<'*N×p 9lR^NC](&V[_Q6bc,sgd;lC[x;z ^0\8lDΛvv "[m.,Zf*By~܈vߡ3r:(L)%=:=a#h<@W _P1i4 !}4F|~ u؇ KsHG XHмte(fr.˦,\" ]z'uS=><3dN\F G(Nȃ,k\ᆫi|K,{W,JKE.9۪|" ':?N^>lwR3nވ@+B *2 22HQ [nM݈*eƥajO TThZն&TBГ | X-ϴRR#J  "s" /Ds.RrR}݀^\BI.mL;- AqYR)JA;< ` :Zy>d>6}/$NƗx HTx/%3wLZGH߆:N?v#D+P2%R8zu]^ pp&N w;[DEy _n{ΣaHJiWMe`P:xnbԸ:HOAܢ(kt mh,`& Mma!AA@97& ^Z}|K3P΀J\̼6ZR]nL 8^MFL쬌$%?΀mxlMF8@_ >j1@ -YIA0PWp@3fS6{Uy2KۺƎ`. T*TRf| auCO d'r#JSDsQf{~)rU[ ( JN4q:^8.n2N5\#:>O E֏#xSgPN$W =TlI1`Ega4AwAwn؟~R-vp5(ljF\&Udʕ #lj"Dޕwe (! 5"1Ek|HhM9#x҉C_\8GtXcM†wj#ɻ! 4^=n$瞝W~(T<|,hA1zWDXԓAi3SZ,ʆQ6h{jf|هit6wMi(ұ>x 6bQrJ=FGRC(/G[ %TS_By'EJi^'15!SQ^aP4<;%kS :|D bvM6_&z}:,oo!u'YoJ+B848(kE8]` N!zWxSf"bhԬᨐϡaSMJJԩK`h0ګq'y'7h} %bwAf.>NPeS}W1ZM};]ӎ4o5RUEp0ST@LK#II` =}*̽O~5ah0T:1$;7 kuF })7QzDǁ?@1 rO.:B rUXJxA8gckMg{OΣO#Q>8oPp%/mp#tQo,C$B4,癏 F"/4yTjWp w+xԳY9JOBO\/ڌfqgEDYm"/ڠmJKaY|b5u8u<^وC/H( n-n琩%e~;ZG#p!l9`b~|>x6OH8'#ٵH2r73G>I1{~<EaA9&h*sr[toyf <ħLd:ߏ6$MLjg݀Dg44[/ɖM?{5[f$H%;%DLeB g7Ո(|u/ ]ՂL 7 m3"<&x.Nڂ.V5r59;ZQߕ PKȂwAp=eyTNihVE躘ܿΥT>cy8 y +C+d4A|A-)i]7j!#Vh$`ٵg򠔫΋ pۋqH1^/@^kJ~~X)%=_ ':I2eAf, wQRcd.j89YqWCDI8_J{'B٥RF~M+QlhƹHIFf܃VTfY\SѴyqQyd<ˠg]mқ{zt}Jn郮ϾŦ"%i:9իP"Ɓ-5$2(,zh 30q~ف0e2+8%'!9VĆo8U@*j=x.<XͦTp1 5B](%_z/H 33ںO7 ģو2&f3CcN*A6}؀m?^r0кxhot_:+ gכ6os.Ɵb|<0Y5X!{ pt6?M*~aJ@֋$#~1΃=N^8nx/҄\[^S0m .󾦁HΕ4{TqzT6l-Q=[ueRA_Dit%WL=\cs֯8|JNB@g'~\`: _g]#~1B 5vu(vyY0Q`m ɺ3pSs{˨4g`̀ ]; ?S @l` f jy/xT4Jb9qSD zȾ;yJ!;+]ێBM oS@tlIG6ݺ<"Vm}PG r`Ev[rK)>:ܿ#j1V\Uj$ lG,ΣZu Uj^P5gù ^oŋ‚؋V~Ԋ?g s W6ꯄ/9\%/JGqFvɱN箩.h0%dxWƄovUH cq2Mm]8>eXj@\f3[įe) @ NяeO}]XATl~ |jn 78x; |{ ˛G}]}/<Ϋayl/ kİ *o: p%O2e&Y;+Ln ,O vC;_*DdՈe:y3^=,Еo+1 NdμeǾ R]7xB&ns-+卭(!|B wX67;9 p!e㸅V5f+dõ]4#F͆ feB6Ŷf7R5GVq5\:?Ӈ*k#䓌w6Fʧ`}̀z ]'.D6} \첈8QjpLv]4S]u 4T(LR;乐-='8%G>ߟ3qcSANP*l>?MirZ /HUҨhzx^t?l5>zi{6U:Ws+_evG8;p,\;p:^_*T^WiK)0sf;qrD0X`.1^ #?)UJY boZJZLހT`1\Pa9[o?JB܏FmRSV oE 7㴰MfC`=VWy[q,b_YKջK e`%O}1 $ښ44udKK#s3&w)*pU>iAIeeF=71J :~Cǚ (d7q%8qJ^jIWsƷ"/N`@x&\(NbGX+gx;,;3 noFJ8{-.qߋ4IKA9Ekg8=6`dAx5E\͏PTen̝kc""n;(~GEg2{^:/BXA^e/|>J_,~ U>:~ 1 Me⪦ iQpS}Cܓto `wJ:yv *u-PU)${ȾwOg-\pQEa!bDcY&$*ANX)pNhvB)8d U=ܠg pAq~Z-܄xnCjd׏bե:]U ?bAaVUt@Co_! +67?N4+\?jb >'AVeCoXlTG)#!B9aOn-DܲJXVVqjQ[ ~Ml4QnRh>1FkKL;}m<%AZy2jvQ/;w–~饴-SQ.b60UwY8PgU4"SwV3<7&JsgZY^5I  - # wx8-vᙌ0wj!*>/#*8ZȘksVp~sci6`M`Ga~\7{-`eѫ}ʃR^e.%#臵mŢE1)wx&6&Ȩ%6vGV 9`?Z8&_ 6-i :D-H. ˹.!Y3)d0aP]B5vyV6~ D'M)eN7Q Q"[u21"~Kl$tX0w=%p7]Z!grJ"5%zU+̥EA~fh>^yHd|S˅kivoykWnNZ+BHIdY܂!n_' 1'x BuH^Ǣk^W_TmFk F~nKx 9!$a@#}ʅp{G D{.É86 "QnZ&^GJ~j@}&L{ p8ev퐃;%;'e|6;G3`ۋLw2J-&44oI(+S6dxΌ)^B,P, *݁]&<6N)=oxݲ񾫎-Gq2/W[]NϑҖ&:<6df*6Yxl-Շ+x ,~]Kh4 U(;fJW;9pKx Zy<*>ܦFQ¡!7((D龮?D?\t{}HJ$dfx։E ,_ ^꟯4 kj*W(!C8.FO]d\Q Ҷ5\(/'W T__^ ux7oFFfLY.id+ɜ g $0xq SشL3ĘcU -'x{Ʒq  3  Z(x} |EoJOJ&mڦ)hi"BK[B6 IʡbQk]u[W]uU]]ou/ P׋̼9ٵMfyyyg?ߕ%q}*5b_h=ۼ!h6LFsf_}W1{nmDӝ.!myMxJI]Bh&<"%n;<&Mh _q Rk+E^ǦQGFNϰCrX>Vcz>MN&&݉:3Xk٢%- ;Ey5􎈢L;./T=۰92A?lғz!&b~('kE! i|?-ēUp^cTYK~}T|#-(84~n1g:?`#bTnK4d瀨We}=Eg(n SĪfv9Dhaբm1TUEu晝28I*+h-PJ((_"hUKۗ@7~9xfB,1(|RBffgvIcbyXlrVuk]:YShlU߫:$\Oתzi]SqחE{1X'7Ԫ}QO{bk˵1A/ˉEw\=[bA o֐} ZICiӒwgW{mƊ Aln4e,P/mH=73t'w5n먩HMQlpc-57d4rHwDmͧr¤xLZWV[Srcy옒;^83n;^ A1T9Y%;>kS>P\pAcSnk`fxBݶPl&lO7&dA4Ɋ$VN#I+DJ-yxe"1#* DiXȱs #$~lT.k55|ų5Y(bJG4ڤ`Wǟ{ JN:^h"=%iXb9\~  Ki\iUh6h*Nr1ꎌuxؽ&Ɇ z5ԏ.jM9Q9rakP9lng 5+s l㣍#FQ5eF78]8ҞGQ.Z2FMd-b5U.u{X^njDl0q0\ȃXKXr&'qOWfo,3a.ĐHԍБjq*o,.)"bz3qm<4YjIƺ|r?) 1ŢW e*kNXOk$^:T0핌v9n2Y~Gf.nvxB`wlю ȧhT[:-8q'*QܽNG qzbvf)4 E5@)JRZ-.⥞6(.b%H3H4c?AgIi prK u8BX~.."FA>9L 6C|\NnpN!w9k;6[G~cp?[nQ̪3[tv-\"{bʁ#QЉ7R6Ȇ5x:?25ؘGq l乯 u3|NtXrpvΠِ X0jLIzdDNLaԼ1+.3d'rQÜ_='yU7c|e]?P$I)= ڛ=cgtYFQ*FJ>Y_j6ɫQiܰ큽#=4 ޏCه!ېö10)]*Wr4;iW-J%[r|k؀5U09TLKzy?F=BVt Zn (ZETtLDʩj.Cd1鲻Pc1SPhzVCVAo9|>wF\(}l^uϢ0VakpJ3i;DBwP"84"=_HGд^,14&M־F65}Ʒ,EY*qν#N^I >YCa˫91(X5"#ajzXK QxCgszcnNc8W좮Fdk)Hp 8ÊHyyw5$$Wb֎^K[{Kg2qwi7Ih';&5ᤎUS>Y$c j / u@&$[^9qM*rMVJцTͤ,c](̋RZH^$31`YX6N6Gi"o( _Бރ3Hx,;XKҹ`) ?hQ0MBl|44*Ea]DLǵcAK"jq2'R Pg~$ .@m8W"yl I҅cFqC[j0rmh6E5 M@(Y-qCUՄ$nUV{iKqZʹȁiYUPPqR*8IXvTҐB6]Иf£\M2y{m.jX)`:)̍ MT6*Q!%PO'` CeCsSJšbPX2OmmQoF=q3^~,=\C!چ* nt,r9a#Dvբ(*@Iμ88_T84N${&lfhhGUUʺwYNF[ee6K~q8sF|_XG^WUz٭)w]K>k4Ծ5h:*}( pxGd+_~!l"=g2ǧ%MѬ5"7? )ˍErXܣ.;x`09U,rӭu`F6 jrM0Dvrޓ Jr⇅83EI}ͦC׿|N l FXiיL/=< +eڭikwa8OWS^n*GF6*`ifg6P6;6K)n uGm~"wdϖl,bxuZe#륑}B%Rn<,@Y5󊜃UP`ylN!:szֵ. Vٽ~aYl>e A>YQL:vm9mtB1V;;AL,#T^Jn̂~BJڑ\{"Ϧ'H>{;9tɾtj_괟k4rh5ƃ׆'IKq+w' y^&rьxR|ތ?Hi7C\Ɨj%l phZF>?4L%/BTLJ p'!n$G3hWBz<@K ,ۄ*d$y㴜JRząJQ>Vƶ P]D+ r3ȡS>ڀTBk܍ ښ-J$C]6ME2ǜQ#hxg L4d?&wk#/gA鐐Gy9 .j qǗ}5-zj62}_WvB <Pt]CdUorT3NNqW× tjηO¸/aZ4kI2x2[s5(Id <b V2,idvޏrxʎ5"hQmN\]ZƏSx^?}kQQ"<$=8QiF]Q4ohRTZQoT3w[c8Rr)m$ oB?v~ ,Tm}Rz:1mM! *lTkĆMY3RR<+6Bx %iFn- ~cy7baճl/ao^ڢ>ˢ6:{SS 9:ؠ/Vj~-!-5(cIV>*zN=O:Y~a=bI[-X΀ը~w);,n)5 HaE`j=DԠR\!150:sh%ghm\kBʗ_PE*1hCǥRBK :l@p)㩷 |3'H dDo" ,RM.#ICpx֡YAf+i+HZoEW2W#lB9e>Z:lgPJP|ք$5#Ð7JUP1n1y2 [YeFCklhՙx*ll'2$u9vhh"i0eVUR&GRFykekA'_g~j7#̺ir뮸[6ދZh;PE w :*Vgt`0j]C-8+pt rf7enL]v,в* wJY冷eF~J27=@ YS> .v#_:DΰR[!PӍ@T=; L\w݊RIGw +1oYt0۵gaeq벶v1:6S%+3 [$^* #)<;C 45QB!XCuJjO1qIl vsPC *A4 ar+xvqS.r quT#ׂt+-P:>~ 5#LV(&^(1tIgP5 ՒL7zB{(.=B eEe(JO.l? Ж7#;wX1UqC劥RVRRG;l"^4]vJ%"s?ton݁f T£B|WXNW݇,B~L"U%OJ]"ؤ \~92'Y8:Bg%!Z¸'לR 9kij1!NʄDŽLh %cky˯Efd)}(?pݵHDIN{*RCg ,=`-TNkLFy|Xa][s pczԁ84~`ٙ,4줏,? ơepw(-lo h( QC(p#euRT=܃nFMS:wX.酪XؒkVw[:zȂ/=ǩrdNNH*%ʮ'Pupŀ^Bj~%{Q/(%7j7yyz,~Fz5B(w/tEca*;1j};=I0vL*o"]_/͐ ?cŤ峂talQɈto4j(֝@YPq(';9bDx'бIWR6EP6uH>|ٟxvemcէ}%k4޽EG\'|r= Zf`o6uktQ^-CH7' Sޤ VDOnb̶ɜ\z 'hVoQb~LXe,G$O­X /B-8y;D\>1+ЕL]O@M2 b{ pP/Mh]rc)%ED:߄˖QAXሀ ;RZ *2 >Fo͐sWb)+Sg\"f*VCn'kG]RP|p*)EoRڝ|B>v`\]-Cݕȍ֍\co}G?v#"B Ca'7] 7 tg&ئB:5;8lT8V_iF+MAdYK,,X}>[vQ﹏*٨8Q@)s*g1m~slJէWsJ# % r̔ FC{ii}\r|j ;^YHtWA&,r8r۫dT{-5Ӗ$Z.HKcjB䫝+>mJĪzAn1+rؿx4M?YJ M@0;KP{ÂH=;Ҷ`[O޾־}m8I,w-ۗv"q\g< f~ J[jO}Cq> oZ\ = 8Md̳'h`40o2eIkFMOk\eAK ~upoqËH;X5@kNXVv Y |ck5epc!>XZrDxN@N.qF.S!ؖ_ˌ})Ԙ|x9LA`ݙX7/ z$6^ZtH uR2| `g$PMw)ۢ׵/ZV*|~LjhZ}Cu rDKQ#YnO6\R5QmZ:E5+`nWZZv6\uR{hs\}ȇzd*99pQ+ bnF۹8 ME]Y+Ӈz<@L\!%£8;dnE희LgE|!mjz>mƗ@K3NgN=h3.QUhYI<W+0'zp2E]#Wùp ,6`^/M85T &/ǥ)KPׅKMVN-1vSjH[q ׃*/+=BC9A<(!؈cd#x3惨vC,0{ ?;bmٌ/&9tPmES'][|8TPr=΄?y6JO 0o+&Cdٮ7/_n՛8^E/O;3U2T51dʩ{1E߁;<{\׳\P|$i)^G2wbmm'+.Ɲ-~N Ŏ%hylz]lTCk3eWkQu.j1"QbmǏ?aieoU]vOsmҧ, 0DM>뀃@0ܳ礩q}K|-݄᧚~k?\@uXǁ酪Rd)*B6\448t .I,`]jK{OOW%_%|v7 X X<o x??o7⇫uS h"Sb?xC.%yU7Qrp˱ 2PX:?ZO4n}'P4"mu;5FV>.vgw3YPqqtlCsm:xnj" seDnM`BW4Jx[tb|'F 'V ZAi b¯CQj~Kr8~Q\ r{ӧDG_r}.dv[{Evgsb~][isiplbk]JɣG0uQg*n7mܭ/  _Uiw^Dǯ6I^9*)pvS__ؽn s?q }l{WiGNox9i$}GYgpnlp>Ś,+>+<} ZwR5>@op6t[9jf2]] .wk|ByܹlOѩΊ3D[Q(Pp+ !XI;4eK#[8rB=n"TnWTZHjl eJs`bɯv׹L z^9<7 pRn0'؝N[*WCHVX)px0K v ya+dLh1$gcs{˚#Aj?RIcPŔ2ǖp]^AL-"xqBZ!+CKO)U:aYkɄԡFX Vyj`v8sMW)Űr-NmJWZȇLX.Y*hH|v¿3`2fńqeg$~NJ"6&H myKC}E9E]XVDJl rJ*RҰ‚&_|mHI:__~9EՇv>tu:LIk͡ձ:̊y= `$P3kccoդ[`dߗy}\5ߨr䧯SWBo%u 7JP1< eoߏIaVd-G!M+:#pGw`1۱ITw&S. dSbl {petk*kmZ0 v8ޤjJg_p#:@8Ů7!Ŵ k=HkM%zWǚNoRfOh7mf!C^ R)"){zrz!7ije2v!iSMpmɀkRlrE]GʂGidE\O/M畳cwQ+"Jŭ3'/AB$?nGȝޜ6ϑLdQAV+@`Ͻxy'eov][@CX@6yqھLw\c&1CgPm@Jf+*io@ ±6FKRաV&<$eM$U_5v.t٭VuͮZ]RDPX6b^䢗|36gBxP[-ӡJ>r^F!F/fBNE)v9&DQ+V7JAK:, ^{CҬ}R3\!+*TŽ> b(X{0*Ǥ7>vgB!Ulg̅"o{(LE b }0\ҍ3ȝ1̛ {pTU4SP ^kUC)P^^V_xъ*BQT -ݿ@铸lLzyJb|w*xrb\%'# kqKRrBqDEND-p׸ rԫ=GTB<(Ve=E<6L bRSU~Qx㿸*Ħ_;'3F!se𩇂ȊEmF=vg*B96p`9bd+Uo3`)3rQ28Hüuc阇vPO=MM爴S9&A|.Xhu]VcxF"o'UKze>xH$!t}p1TZ)WrYeg3U/l ye+Xm%L>gU+_Ua'')d9V*7PҲ"9 ׫p"5rߤd(d9//Iܭi!'(L3E>H`I HK?JXVȅeavaE'ߐ6ʋ!IzXGUS5Yʗi̮$4MT|l\\7$F+yVC?W}1UWAݸ:U$S #l1+K+d߈Oើ ,V1!rYTóY>n'kjyIqFȂ2Dt(t!ڟT儉<Ti=K1_UC= adIQdxW2y?Q_fg9<59CQγG6*,< \ކ?rx6|b]ROv?O U?yE MX>q*kw[rDW29QL:B%Ykѡ:;ѪUH]ü5E5EoZU×r"I\vf3"IMU应LIJ:R3G-.K|ĺ}Rgs194HjZy#xQ,K9}؄;N?gQi M:Yg}Y*:2zDRQ=!bQCE)jv0wNzETW ɩŮXs4-&grRaCP=RN99tr,6XGC迟ȏ7Ӊ{<vLIck\6AAIH͎HQuundtYgq[dQAg``#d %ru;8?"yn1,!LK90W!Y~] B]C;Y8 _[ƔbsA~I:ݏnC2B&Uf ;\C9}&0MzO. L'1^N@wXȆyB' ð/S J2zX$h٧(LEpFa1QW)90%UHbSO' K&cYf C ku:ؔ-&GHmdi^dA 2/ÁyLJ :o,^cu5, ¨J`0un/Ovb9ZJD7 +zB-TV x\xL=B\ԑB(~˶MB*CFʄ{BUp-$f[V4!sci*:N:8[$LQ UH;B\r*B3s,: SJ&8U/tĠ#L͈(\2iy+F9B;#@X`QDAXxIXU(O*t0=}ꇣ##9yc79g oL0}`HgTcW8 `"ߪ{]66IDrv,j$/-767]T5UP?4*A;]rR PxcP _Š:uz\2iY>QgdWP$|xuYX5MskiP}0H4;9h 䪗az<h(Je/gIYpPIt0etĭ&K9LSw86%}e>jFAխ!J$HަPAe02(\2f4ZWr@s$fE0RASK+k;d!!E ;6JF l$Oo\R2\?ճOX&vtҹ iWl~7{E/MIzVcKd'Ig$t` 3z5=|~6Mn ? y'vROBS_-EXDRIҞmDy=xLƬnlȞD|>է8)*V[zSD!l(*f? ߛ}` .O}~Ԅw]rTUfx22 2004/03/15 22:11:46: ~"O ;1Rx{? F& FFVV&fyUNW>yfdꅓ&ײN>_- jvx:uszq ̿wt?SExcÄÃӗ@ly\m)ba[y)n3|P0[) x:uBȵoYVvkO vqCO'L}*Z*X`{h[D\$ I׻\<_$jJ RKR,KO_phoyXK”\V/8xM/A3eC#9M$$ܝ P]rD"4cvN|UoZRSjD-(T& /풋qf%g $3' HݤUW5w4]aYHBhhՕ`k6e3pA{sc@ih:tR2PLqn,-pH"I,@0"MXߓQ9^"x]|&+Hy?#K}n!>Jk}?]γ;R/X{pxdhc/#KJfq9LŌXS2R83?Ogr,U,Rt1ndcg]vr5{NKL/-JUHT/Q(.HSR2'r8ʆ%QccJ8΅AeA9ɩ`A9"Bb^BXbVF͟X53J2rs57aMN7x;̷or]̲"Ԣ<%MԲT]D]t|Dzzg&f3Yo~خqsh =x۵< w  L M ,L,rRR7e.RYZřy, BP!%#%Mkb7_Wl\̼|{--7?qf\Yif{'a6S/x ? L M ,L,rRR72nOH,RH),K-hSG'gpm["(_x{qmN̢Ģwٓ"Lvp%s.y?Mx[ðk_o2?ё8&\s L1cK2*100644 iprlib.hKUR$)H0$A7(! rxeOhQęa6iS$M&آ&dSJ=i4\5ݜJC=i!PT$HE;B(hj" s{#Uںm{Dۃau\Bu/̏_2 %Yћ T$E+6DвCN|#c]H%6J",BI!nJKq棥Qi [r'ڮ/{Ňڋ5Sk]C)]E捻MtzYGDVPi5E F:X8k&9EGa" EhXUtS\Ԏ2'҃FF9N7\PA3gNae\ CC1># lĈ765ZlE? qFm{Uؐb8eu4vz.{LO,,7ux1u we  L  ̬L3 ocsOF&~Jxq ?D  L  ̬LL62OH,RH),K-hSG'gpmSd\WJ23x:usk1ܻB x&_۴]zC3̂t {;>~v9R5#3ĸbkG~=Si0~9I@^~Ò,6/dA<+x=RKHQ;?#48QL%6]6VKVh1.J V`E? -!VւV ..\Ѕ]X?n\uf}s;ցgUuzS n]=nl7ki_q;G xL{ gYn1Qu8%0mR18@{ѩ]w4j9Zz-s嵙?UR xicE:B702˪CS{p.5OPc)R=pOJBY @2V)"@E7yc31)Q~5$zEa1͋)$G/3~rJ%"Vr5O/:U%So6(<@fzK[h\Y^‡{Շ v@%A*|K{yjS>+K5u_9F3M?ǁ(Uo)U|vxU_0R`n>rOAC~ch)orC}5Σ6ݺ /xxi $ 7ϗh8ydl7OgO,VH,*J(T(WHII-Il`hXJU,P L\hy*FĔĂ"s4X7/׋a49mGSA\u962aaKI-LN|Trʌ'$9.dJ 哿VvnJqxQ؞39 2004/03/12 21:08:191unknown27errno != EINVALjطBI<x[/r F&F FVV y)EE7Or,P(J-N-*KM16|N}* p~x[ӗ48GKm"u38=r3z]8 100644 iprlib.hDO"S@)Yvx8 @շNPprot_level_str[3* Zb\xl؞38 2004/03/12 21:01:271unknown27errno != EINVALjطBItarget = res_addr->idKg7'xxbF F&F FVVFy &9On(ؼ*7ax[5l z8{j80oy wD RVњ111&i2{NDmSe24՘IZkoh'{{Wk>΄OƏp%-.-pGƼG:)`:]IE:F]PϷZ 6(8ZGt5V]ƶ1@FSI?b"Att^{t[LWښ.9쎣0SWv-3,RS+jVpYmPtJ}QmoϷzlc!ڽEJO{h7HxB/jkDAp02vB6nY@}I)烨(=@4p}찮Dq騳|\ *-ijnh 7ir1aaU)ݐcD ' e%];'M~ qF[N5Aևċ:!?`{+USE"M ed Q?I&ĹRz0 O'5\{tDp⡄ zzL:-n"5l{KYҕso%~Y\(?O'֞Y?8Abߣpdh穊h Ö (4:[U0a΀̓Z3L g}=ɬ b.*p.#5)eFH`_F@Gw¶W͂]qE~ X$Ў_^0N@ANo6(q8l.^E~7ϥу 6ep1X5E:}w3&Hot~k 4< λ,y7Mh,t)! icY`1яXNZb GoTlCCiEᚍ ;T xsG܀$$5s3^'1Z!Zz68Y&+̰ ,&Y2/ă+0^D0SJ!(J0Ƞ5 jVNjr^դS&,H[*2B:HcV[].9<^ ɒI)2RD)F臔w aA.?:AYP{MCEYftTq-?1)Dn:/c(O['s /KBv ƊKxAϿOZeG_#΋N)k{uj[Z& @Y'$=v}.?_[EѺl7=ށwO]{|_g:k6 ]LF6INr5#l9F0 nmC{z79B>ۍ}_Pyƽ!+3|m Sd~V.vgN΢:;FsQ^ E+Zʻ ٜ?pt{C[q(PE ^MLE}Y rn G"CMriSgkPks>s:KWs$\}*Fdh,Q7ӞMlDZ( ulY٩e'b:n-}qW{6rIRlsIt xfJxmJbCkK _8Z(jԘ-KxK؞37 2004/03/12 15:40:131unknown27errno != EINVALjطBBTxP28 2004/03/11 23:25:36 bjking19 F reserved7Y /*qrCrr % kx72!t[6)BD1dFdtC5>55 Gxl Hpfound;F—m[]K!0Ó+ iUZ5g}`>V&:(J}X.Cb]\DzۅMjDZUcj@G7r!&7X<݉+ {KhMN.ߜ⃬ܥN91X4s\.e3(Íbr a9&8`I6T~b.X`JqEN\&= nd-P>vאx oV((q &ӥOێ0,C[hd+&Vtsc by)C^ ݫ0ryO~MGX@`{⨡@p??u*a袞?Vn1}[yQfp,32 pI[o }Bk o˷_l`{)I]!CR/G+GH 351Y'^s:ǭ'fA'0x:us ¾B'BfcYbvwSC3̂t n&*.πy۞ϡ2[%Xߊy,#y^lj9I@Z'tU֢$'͓5v* xU_HSQpt;mM AYs7D#% %]MFs[0hH!!"iRD *bXD/"=^zs~sϹ_|2O,gr$\<=rFd3{Cnxv ]]#Q0`eO5 B*Ni}I'a,lA%hRjY5s]H;wmvKؘcvPEJ *CT\VYl/K; ˠKZ!lsIɿ]5Us`oTA^"z yBBd&+s s sp3m8D9DD1N1N?7}7#`eּ1PWaռ@d\f0BP@c /"u5(˒~5(*jĦ|ٝ`Z g,ÂP,7 <P.K\j@I`G*GԌ^*;A3c[n&髍4gxku]#}c}CC ʨ֏\\9ն+p3p1rMdhlg,7Dxo ?D L L+SC+1m:J100644 iprconfig.hmTw[MCQtbPEp1"n%-95xV{P{.˲˛M$}HIA̫-YYv.RC^NlMѣՎi#5~jEӌVmUZcQ~HbGwfwϹ{?=|䱢JSY,U΍ W䊖򞵥򎖫"G֊D>ڲBNofP+BJN) ʕ._ܴWDׅP@pD~|t} woL=ۃ7&WGu_Vy4╴$.X."g"-ϦpelѠp Rci? MSYO^xK[}8 qbNE^_Du$G('-X01Fo=WIBwq^ uaO#I~L\QV7Hً8Cf:-BRbR;>KlC4ʘH4UIH" l$ GI*H2O~Eʽ[ 7$! xrxɎ\ʦu|!p`RRN?~@JF rDyD|_Ǫ{ EԱHy3v !5IWG͛-f|׆-?i]v|sW.*>x]Dt51pHe`u/CW Xd7S>F3i t8lBO#{P{G~̻y[_& Fa 2kU ]ou&0*݅2T(ϯ5Ip=0:LU]9z 89MiR=''H!XI;!x-(}O8R~eOAfz|<! ,j }rvQ|bʦqt6ƴPtkA]݃PbSYI|^APXȗmkwiP@V-#sG!רu`>Zo˜'x؁E<2[=  uj/7SL4I391VxS&㿅,$y 4 ǫO s`/mV_Pa{%X/7%Dg1ʃ 7붖[l*uO%q9_wq`p ,P*9Tu*90s9PŁ*IX͡j9c*B ét+EA9s= 0a1;0@o@:gN1E')xj3oCcœ =Ʋy]R{$<`1s>v(Tv0]=8d"`ٕ"ZkԫMaWÀ%e(_B|jniv;XQ#{eppP uv P }AG얄pHUԣ8~>+</;iLSbO5!KTL4W[@Ur>P]Kki#5ԁְ=l 5N*;Kڤm"56>,vӣUrlNG_x[дގ!(R+&uP:GE[: n1I yR;OxJ_%yuVł)؋ĤMAs>F 6 诐7DPEӥ4CÞ-f*tRP#Tx{m WR/Ĩ<}&?0n4ўQM[ۚv>Fc2j_l~d<=}mi&2LlřxUg:t%%?/ur 3xKR3f(%+*Lozzo`v?L+iM;u!/B7P䉛$ bllLj722KsfP>oYsmMS@2HT];&''af 5 !n>}2%0%o_)h:3;0LfV(l<}4(e"_`ȢP3}5if%V}遒̊3L+,! $Yp8}st.i &%M醁y0s) vdaҶ=~-xUkLWν( "ﻫ]vҵAEG#ap⾘U jմ6pҴŠE^Ҭfhj11Ӓ1bHM:?s3}=gmrmi<~qps/7$%8E}.BCYx]A%XcUldLnCZeKC|a)rYc]6-40-j5{QST3 \)Myl&] k${x'uy=T^SҜJۥ3SP$~UpKA ،c3JtXo@d݂'%5&i%Lᰅ٬Vx+1yTUglOB"&y6kd*)1SHj%!oOdڌC\k- .qaԾOIDƆ^hHgw m 9%V۝޳hy3Jݭq(XhfȾsU9O$H=aM -G6{@yP% 4=>q }x>ۄRU+@f/ՠI6HSnt=PD)\MRuG2?Gِ_tʆJR7a^4_s}*(K)<&_} Q&E {BL JsyIprm+6f*oT?Kև`gb=q Z|9QOZ6=DTy@0v Gq/Ht8ѻ]✂+,^F٤P ZcgAr?^M|q.t%P@7u'q7ny)K-stb(0w}.Ƞ]NN:q& bf4ƆpRTD>Wf Q*mH%u丵D‘zV~g0/CriVæ3CG8FpCirBs `g 2*?,Po{6EFj1[q|c]91iJמ_:( .C9]c̓_)|S>՛a7ϠX%>"J!n-SeZ?'rN~[~iB9~Ro^ѩ^pVLxM+~^մ|JZhP~ Gb XI`jOא9=U$ !^ XH܌( U$ ^fSU$T8]M2kD@6xrBs%RrQ@ Nmt Y^XGv[Ze\p>ȨHܞQUo3Yc?vΝkкўj&QB8’BŽcmZ<)$CFS x%f7i j)xxY z&3*L6/ٿw##Bhkq$ox:usڱL^q_ubF_T; LL2 22~vTcSrL!vb y!s2]}^rr\Nw${?.t439Fܿ&e 84hxURKQfƬk̛VҶl(]m 8Ύd/EE t/a2"J^#h ]m>{;'?8-BD/ Gb F_zrq{ 2 [j`rdX[㝎•nT2G0xD5~?w!˲l p9nMd?xI+s' =^|ǧ?X',[}]3Lm2cRgҔ*n_a_lLKPrʘ ]'`7 a5^!jC [WD.9F5X] |ANQGRcImPJ |YsMmm1K<~tn.() VZ+÷Pns ՘n8FEϓX4#J[l|@ B=E c=PVs{FZܳ;8C )8 Xeڀ|Jo`4Q/E޸CtiF &JAsedj㰒j Ty 03PK!؋3"+ԘJ @MsXa %5i2>@%@<^BNf.U?|+]'RxcؖĦ1 ax1uS3;r F& FVV& IYٙyә|YKe$;gfFAnAA! qAX# 9Z#+@O7Ҕ (襕%**(emfor}+/'Gm>qdv 7+o19!fr\YEEy ~a>fŰnϞ8k<$$O]JQqlfr*xqo ?D L  ͭL72f2o^aZWO k %xG;PUa5|uMq.@100644 iprconfig.h+UslƸ&r({'"n xwmކ-L~Mcg.D1%IxW}L[׃c1&N` I%$!BNa?{%%S'ʑMmM%.6YMuQ7-[5mՖQUMh}`Ѳ}{s9KܯsfQ`Š+~+סH0Q4:ErA4U>&O2Ώ?|@%A o-[}Vo۬S[v+o#pLb$j-Vm%X7˥Ep kAUW}ٍh{Ez}ޮmW}$B.Q8= 3!fj?f,E\qDL"s6/w\%$h2l,ڀ #+>TCVu`އJQEIH {(tCLHX \$D4J D$bʀjFO%:8{]xwA_u2ի|XiRk.,DyDq(D9M2:^/CpS: F #tM0~s?Z5iHœϦ U$ZPXәU/,qZr;¤\$=l.JHΈ$w84O.+vJz"b8vh~c4xkV5M=#- NQeWZt n*u@]:$ȴD:cQ/?\Mn|Ha( AK"Ga &7|b@}` ϼm ВWڳM&`Um̅q2h"H( n/]O%|^gk)hjq&NL3N]V1fxn͓|BNaۃ.i:<),-;N#{ )F :ջ̸߬NDv6XMIQ$D'xigU u9uBfΓAGZM܃!v<{f͇Q-FK+Q=+ GR& to:R[+KRFzseֺ= ӥ!yқײl9vn"TKD+埃n~Z>O=m,h꫿ט+A'|(r.iSq6^</LҐ{i۸52W,Ux,SOG6j/óK-?6!~yCպ?=CݔIe_Na=W}P4p}6N&I=ΣͰs wFoSA=]˸-C"')~~HaD{F7T9[wU}dUM|JPNcJm'C]Zh Gi=cpjNy`B%$Х֛i,7 ZȐR68҇ʰBfrԆ*kijjen;c3JM_/);Z#(l"M2EjflL䤜D9JF҉Kc&BmnB*ɐWb2~ IA.o+0c.r b<1]AU73,6cx[8Nn%'jvv,~=(x|1#Cͩ3=Ջ_|OTGUis.Յˢּ .\,izϝzp1ta  n\:Sk?:$.^q Y{\w3!ob100644 version.mkv&K] 5 LM̺&=x!Fl1;#l{9&/.*řW\:YS&ˡ,cW\WTx340031Q,(*-)+.HMf~C[/gyI]132 x=!hb<,o]ׅQĖur^(?X2 /d*b(g&(jZsisqnba^ \8cxN2 2004/03/10 22:25:5BoN100]M1Iqz!%1x55 2004/03/10 02:56:1 j@E6+ L19x_;SkOo{<_;@100644 iprconfig.hrbIL,jZKKb"ӗ y)*rx`1 2004/03/10 02:56:15AoN}Y w] 100]M1Iqz!%]x:us|Fy3#f&& Eyiz UGLqGi#rMdmJ$ĎYktwleEřyz ""' kTg׽!d2x{w+[W0m]lYe j/x{UN x340031Q,(*-)+.HMfЍ9'\ݱy.hl2xǽ{B(ư Vlux-?fד2FZ9:aP͹[@ZO jVC5gf3$Si^6jS3nxuQHSa9߷oJ.d^'$cI EDAqι=5uMĢ/\ȇ| =s#B !1dnc=E99ܸZ=HeH/Gd+Li{yf%\2: ޫWnƓӑ7½xd-~ˈDži3zh҆u$z]=[ʦܔ`Wzn;RI׾aҋ:={A( m{@WU# \&m11Ͳ,ø+F/Ou N쥇y$3Mw0ߡF7],c&3vj wܛ$4O>I/J^ˇ&\Xpy+p 3E#Ϥ0$) `|ɜƶ7IL\p,S8<qIBj%+7O[Ϡi1L~{ L"ۏ?D'Әe2mmF|ҒVߛx5c8\va%nj*Yxnz>2@Ak;?]c0*OTҾ+t?}B [j›)|б|90n7A5sK vJazS$X0d 9CM aa9a4BIuC678)N{YIQ„jeXgn̖ :=Jg8-=)y-Itӱcj&gv\A0a׈D ejAH.oMͫ/uh4S+LzZ}ܬqFˑπOW^/])NoJq@ ڷưj/1^JO0~=mG*h{}iz(MZDŽa@|L Ɏxwۿ?M. yyjvC|( QK;s3ƺt^KܸYI)X rxx" (`I'##!9VXQcЄ]rUl3}syX#n UjG?Ve!:fFA(+ѷ_ ڳ -h$ R3A8]5YS#N}}u̬`-2:aW,YE(+XؑњGFؤ4j{Fj<ҝ5pvjp[Քψr0!)MSSIU%ATE&u6aX]\%5: h6|Ĥ*$`!\zT&y'\aƵB ? K~A_CXiK̟Jߕp*愤(@-o @$LWne ysZGg!emV|h@]r *sX'(k~DT] (7]B^cl yv7V1P-hݽ˱1ltZ?M}0=?!GHwURh5ӂ?q/jŽ^?a!)Y\wQ`>n6DXuj@LGj!nibiG~?c.Ru-r_t1cX=C4:GΦ/K"[Eem=OF7`9'+xVMlEҴӸAIMNiiTr'5͏k;jT;wuҴ,悐 B@.p8!!$$$Hx3kNmx|7vvK Bj|R @7uG'~8eB YP Ma*3}@LQ0sE\y-hPf2#%(*8k8[taRš;@LmbP4 amNX*ț&eĀtePԴ)L."`ekDZ0e!j V)c,5XՀ8zVY, c`GÏUY E*"+tM7 XPibD$j*wi~!Eds8t\zT6t1b: 1L\EL*{Tn.|NdrD |69 2*vuArjݰ_DmРHV)ڝ*H }5rB \EtC80-'kLq,XzcI9)3?CXɣيU"0nَ@&X4=U,dG#Jl$v їM֒<%s-]Ξ7ZNlqwOuwG _TʱC- -]`u Og|vWtv+>7T-eja Ӑ*~Z6pQcpQf9uv~2)0bEP8ǁY>/DB *89X貅Q,M+?ဌ. 7M^ivU{'^310mVDh+YQ>XC)ZزCN}ٌE5ވO3XUMU7,dXEmCxz1e#^nxx>NFX"7U'j$TN&ӹK v$Ƀx2S!QIq*)5Y3,ps<6^#;7~h?a0Wr]wvV*;O_ ~2J4-Xd!dH&wAaU[ZO <a}dL!& E214 Pu`:=9ٰ*^CI |`C V;w_Rz ^Ul'-78w>.G7S>ȩ sK&4e=tؼ|G?6(%I]OP69e 3ԇE'~TSW EB/u-wnn%čQoc؅N7Bnko;ލ6(-*2ϻkEݠMiN|E3lx[yy²5̖ F&FF@S &xTS|!En$Z!""*1}%&D Z;һX;]{fgOMg ]Nz溶:Mvgz֢e%|}wo4o_`OYj-Xm[JJ+*|civqnɜ] *s^)!% ~Z8b~6i tװ+&+VJ;c[WAo)-]FXXU_jv4*x,CȢ+_Y8S3jЃ겱xO6"9H\TQnKؘ9QgKT=R-VڌtFZ(%ϵ>KMitR}uq{RXg7#-N`bB|@蓐d4A91((<" 16юSmw B=+1DPl zN_Pv\0plΆ>:P@: 6 ^s_5X~y/9-,p"/Q+o}Qv:|hS^RZ)mKT+E1a sZ5pB &<>cq|I l(䊐J{dw gjx04ΘCĄOOC2mZYgjF\:qMՍM5]D {gy{Œҁ`[Sh3M3j`i 9b%3IO1b6/DX{i:ˏѤ#HHݣ=ؔ@ot|%3}GJo즗^}^Gk%sO$CʐՈrs;_7TȨQtU56mml+dPth`g`KCmf7KohqsR3WSZ5tFƬkj3P|ҹjxT)Myz /Zb9gU[__xɝby9ȍxx/HMfjZP 0a]]ch(Q356SX ,D#z",f-WZ쪭iZe5G"u mp/@TzEqXc>. $Yts^GM3갮Nz'"9 vXv9BFqw2F7w)U8 g:[47i3Q,o"?kS_o+MgjP-H #R!k <)ysh;֜Q{D>1(GrfveA2}BPZ!쒃5O+.ƩrwŸn 8qs|d{+^2O_?Vi5z<7R⿟T[óԧ邬r$y/H4h}0&D==t|dS\pcʃrɗy])?vbχTQ ;PV ݴ5y+JȿJN6rsȟiV'/fYQXznŒXH~ϜPD:B n ޠ~AZ8٬}6FGk{̠)N#{!|kX5-:BVvĤf-l o!vK&W+"m',[!\ E͐ BeZ{NЅΫK^(@hDR+ ֕mʝV)_` v<50 )6' LF,XQ[3m*j#/ g'^xMEY{؀DVe Lv#1$#1aQ2)ju0^Ĉ b=xd3y ^<śvXI{׽;J|}3W$;LAO-ImI)(;D" D  iȔk Lᒐ!k_<t@Å\JA%nE,U!WT'ٜQ;%hE %T X$3 mK=F~;60i6#zW"hQmQ10.7u7_"FSڧ,EllN؄cT/bZob/P*Wj5(mT_~*jePVuu &,IhBQҧhw@YDopw @d>Z8[x3PbμuӢPHb(8/k>@6N ٳW9 lR6?soz̟PV͐f^L7oR)O-q:(v.cRၻRkd?pF]WmJդ77>}Qᨹ{vAgF eDS]ܛ>KL&i^2'G|Q׊D n̏9 k4J0enu^4fFߚy"a q#N[9sֳs'%S+卢`SywfVL3gc96~ڼ>>9{m0~ S:xI9iE4-[ >*}lw|:9oK~7J=~x[e K'W3[Y(Bnb^JjQq =K *53SRSKu@T%%Mk.NNS6OW2_u:y0>ixqO ?D L ́L+S+cͼ\ D6GJDl>_>0xF;*zOiW:r100644 iprconfig.hs$R&7n!ox[|wyކ-L{8R3K*+a2BpGa70#;&W71l~x"ӆxɉ,)ٛ_ 2Mg ,)a)bR,. ɵ,I;n+-  x{ED71;5-3'AD%!n 2Qen߷Z?,(JKL`PU1@#mT6bw y:@yG3]b[60x÷oFFfF f zxYkpWשlIVR[mYu-ǎc+jeQWSvL44]V,:&:0hsB` @i0SB´LtCw&xw=;s߽rՓ5M]i!<4>3ݚsbޫA=t׽ ٽfǧ&uyDag5/ R%ɋ)5+(+eYEA2.2Rx\H# #mwS'& `oCiIPz(HQe$XWc. %% 11L Z;;ccInfCpfffhxxМ};Q .v`'\#3!1Ph{`hMZ4'''f'Q|RW܋1VQ.QrJ0 C^ `_`/)U< ?x~@!gI&I Xd? H'J*2؇xSSz ^9n+.ru[_ֿQqO>r~vodz)KIb*/"nbD^] =p{ȳ-(  ܛK 6n%qyBp~lVTY;`>`:bV7JZ]]ҼִU)&R4ڑsjl(vT0 <羉焝 4|A|^8>Ϻ𮧁QXd%Ԛ[A|rx1M>5dM<qmٴ.j*ny87ǫ8CW0TFz_7yX"IO4E^SUSkSP{{z?0R^xD3>md_"M揅g}EYmpFP|g%IǑBB;{,gc&&J"BDC];ST^+hZux=S{>+CPf-Ǻn;::@Sb3f ]U#͝r!84":[|_iDvGZhDh>$\˼,høs(u8FpnY1bدА+SIaa%>,͐K|nm5b@8Y&PYHĂ[i":{,-x^;Ē;{ H3t~ vx #%dev\ 'D V0BHuؾ~ܘ`uy)Ǯ)ؽ`Bqxx[cp2z6v{4BKA"'U=zOK~7~T!3v.mx!KƓFmjG`dt<ƓH<nv:65qr¤'N amn}9\>K◛BqfO):LJ`ΐuw/M6O/,fM@-}F!YJxܶKڢ+h(k##ɋף-ni>Y. 3dSfa^.%貾tF^oD9,-PP+Ȳ$W)dUnGeH\ :Ў `S\^ƼBWh]/6<@vfu! B)*M&ΐ V0/Kl@R#&^*-5xb1ZXXP0X/*,E!ڟm˝$5t#(ȢrҼ[w[Mׂ9|-V ӄ@B9Uқ1M ;͕$ժޗ9lw$tVtM=fŴ: -u5/4rU7-eۍS, 5K&6հCryo{/iô/t]fފͨ@q^ޅ7ZcH "_u?+Rv}P h3v"&ۗi5x?bxWMle&iIL)DQZTYRZ+EElcZER`lKC mA`i@ @nECKN%Kr rf{}fgA)iHyMTg8!)訓XO!- zA IUHd8@Cf;0B&f 8.N8 z+QD7A&7 :ndE:jy+(iY!ed:IRj{4lJ:k&h]FgɁZ %ФB0 Ä2>+`^4Ӊ1W7mP ivdI$˒Hf3zɎ< ",RE04hh bI`5= `KQ2;1,Bb} CIAdhM6S$Md|5=K%#*ݣe,)%4A1?CXYL..Dj KYZK8Y'Sx1I7k!B6(#FE3f0ijՑ&YabE*6*Ԯ_44CdU5E\)C}MBjuiIuq1$&JeAĒn@4 f'p&ds#hNJϚ.ZT!}h*M5Y' l?Ջ"x0ˋ>cA \6ZV{)&pV8lI]ʧ5LƗ 7/6+Gg4x`1EMSANJgdaW ћG!{]CsC!юf*5wښa. G3〥!5J/0F\D@tCx^uC0 z=wgn%h2)}~7f5AJzRͯ{ovD0-yjo3>c=5lfϧV+Bߓ4t߆| }aKOËK| $ H|w/Zr W`or1'5j~}0L>ہdAD6>}̱r,6`by_sv,n bC=XhAq;CpW= :::oϪڬ:eYQo< @"'~؇M:02g1td+nJO߰kc̠^U|Vˋ Wo"'3@QN= x"uI1)B84%8}-𯱹Fu/Ȁ STAjUKuiټ|Bn鯶m0MG_!ttLT9 pP$e{Op\w`-anj-$ cFܼř9l:C hyg=#Wmf@paYy?Gfu ~}2X<>Oe CS8;mu9txzZ˯À=-h@,7 O.:=gc=Ŭ 3/WkxBXP R5v*3pLN8t{Cg۽j)U LGp85Xlrlp==Z{UΥ2Pf<ɹk/dz*}2+a fWwxܣF}܇8F8A?,dk1x[Sr™Sm'&Zx qDVPZK49< Sx[j K'W3[+aqǙWL>Wz{Ix{e"F[S}K}#Rۯl6 mx{j wE L HOX ode4c1j.x{e"F/҅'Y7|eky_/O7[7-߽*̛PZʯX"lJYl0A`0=hQ~A]d'*Z}+ph&۬^7JyQ~א |l%$31:=XVBq_- C/7^`Z]-+,& ϨV|}=V c:d11ZĥvR ^?7xSOA?*jLQn! =-BJ"!Lwtv-)?P1jd/z0U/^=y@g ͼy{7?txB9 )H XMM]Ć&s`eWR0 bQ.P&4C16RH*jx}Y@m*[%fPۡ -;9Uj JR! B&q; Ի@ Gݡ59DdȀu&,"KUZ u`vJSUa*̲PpZqHM%c5]X\)gְrzTɲ+m(V[L!+aزTܢgKšTZAGVs%=l1]Ϗy?\4$r2Q% lPP ^o LU`s`jg$?9nni` TDfwD",wd1-)ibhLjS8g~vߝ?xrZP %zϝQdb]<7]FBY$Jk>lt@'JW Qg&PfZtzpT#V`r{Dm] ;o yY?}B{=޷݁vއkoVx340031Q,(*-)+.HMfhS0޼GUoX͊,>ldx={B6MV&x340031QMNMIeQ93vIaHۧ_vtB:2\k=m_aGkn(de0,q2>kYP[`Y˼u'ߟA(IKL׳`pqCO$3/!et#su*{҂ĒT5%Θ#>cu'v>doM)BnYx̽IYq"sVnnx{}IYq"s&V$nxIYq"sx)%\jx7oFFfF f 7 Xx[SrD--.Kl&n8~bdkFQ3#}#}#SCs+K+c=bod3 5x[yi¢5B F&FF V3mYZafi=FE>@x{j '.5V02007072W02521iJԒҢ<Y BxO TX&N̰TRpQL()VJ-N-*KM5P02007072V02526˨,5@dsGPfAQ|qjIiA|AbzAL͉*(%+x;qO E  L L L7 0:mw N'x; L^_bϥWZ\PZXJ2s2K*A=1%8($3? lXpI~Qbzg^IjQZbrBpAjrfZfr"HFM$CP]8S B:ᬩ_PYQ`d``" ʔ21-sr@&+40N֢ ߘ+ZZXL̸ms[.'qc8qdžix340031Q,(*-)+.HMf(:sAϙ-&n7蹚}8Elfx={B(0{VxˡwMj@iqؕK0xR_`A VěQرؕD.x97iif¡yB$)}y IORpx'G&gmx 7E L L L7,0nwm=qPi=^x340031Q,(*-)+.HMfe@ң@SJ}'&Wg:xkC+ Xx{eظ~/23m MnWs nT'ȫx340031Q,(J)-`J[=И]Y L|L•de0L\bn۩;XvL %$GR&-g0H(rx[׸uc*[SQiLx[S[y3  x72aG g HxNrFW ]ߣj*jz]UxW͏G~ &ۉM!׸gfc%Rfpf(HtWTUսEn-pCpD[? . ? UuƱj^K_~+ރApgwO`nsF,܃]f >|xr[oiohcHӕ 3TiA>DQ0h9Dn F?(EEY:uʭ3#:7:?nBovut~S/X?bO!3:a>2G Z=B~O0gX9+'ۀL ;)z-:#@FS]=EPbF&cD6;5h\=p/6ycvy7J7FSѝ"J \{aC13Y§ `!6:Ee] gik *#UW/FqMцw Z1 IE5 rEsDT1Z%pV_x)ÇkgeռA谏B9"*avAHuMvn v"#z ; /V 4:vv97at2Fb`z#6WنξU8 v!gNީNd>F[OevJG3bd?.hs;$*lMm :S"fJ<>6XX Z}&E$|1/8z&fΧtF8D졎:-T0H bƼ='9Clfhm"&^9y?7DH M:5{R/x##4㫱]miw޹?'EzQCޱ>萫C C+N S~vKM&by/C̽H v RW]F,pr~UY(=3kU[^m<$Y@K>LhmR@@.`h53Gpv-2M1H=U. m|;s*BR~Ƅ<1Vfc(7xػܐ?K`Xg8Oή,AsߝH|u0m5=uJLFFg,נhC9̯[-yT10X}/ZZyV\ ^oWLԹ-t}ljtύrIZ˥ |p)(fg˪[K7)Ϗ>:64$Ew{{}ўQ%qku!JCm A&aMG $F[X'*AV8ϳd27'iWaq+ε N9~R*}x{՞]/C3 h"b抦(!.S즵tvb㇬QOeV)*쏒dS N׶y&d&Dg^{~QFeѝߦ`=~yWz_OUe[| %$qws;1%3}o0QV[t݇ 1\\pp _鲋~l`S}'k4ԔԊB\ZT͐h0雋^ujk<xgs-;g\rHU9عBx{q Si f{c 7 &x340031QMNMIe(nܱPT%' gQʐx?<³*\/zib y%z) ,oru\d3C)IEyiz Z#xq -9Uʒx/eykc}62,!wתLly_-y/LnBYJȨ7q.-/Zo)۩d$5@gf4c+FO G \%3eݴN]l5J*B"sSS"p8$3QzxJyՉ~y6/`¤$^Buv/ʨ,sTT5 r'/Jo W{O!Ԕ$]"}C;wc.vZz?dQ 5j `n$ R~<]vяMlymRZQ[`Y˼u'ߟ)K-*f8>ؤO[={[px8YR~ W=-қ*C5 ˰et8ꡅTov6frX|cvԇ5p9|q6jiM!MOt:NjgêvvMR'@3^geه}@.ZVosv*Bexa{V]FT]GnY~ RQTk 'LL5ob$اWOϭ#k)#{DUdubP-?%WhuNxeqy3í圠(`>%,f_X5T+ ?^.a`2T#D3˒+XIr?m( cQ>Nܝѳó'=y3 m:i$Exm7ZջjxuǮh6R?P ط*n?_(*]/kc#˚wJ'Y Z= Y\jߤբE^+6UqIUg]',GޟEᙉEfe-2LkIV^S .@ 1%Œa\g F7H,$/m^'Oc@u%PRL`~3e$?sCMC 0S,wj"WEZ:Y ybe)_.PFm(mHO'|ԟ(*/Y1SeB3m $RxEG\/߃҈n\ΕֈUOBj QOVu4=raUJ$N]":REݚdڬByaf6I8 %"ɮ$ |I]_Pz)vZãcUr5xO6sg9I:[>왛.& ,aY@v.HQl<+*|ad} +IP#myՇY|{̅!ĵSכd_@Υݼ|fdݣcNN\&giҵ!U) d2tKWv*2vnvt0u=G՘3LUC۸lQN{*<j[#2CZ6 O7^h]72c[X(/,^;5CF^eRai'sw=i0*k9_HZcw@ոI4D򁵓"dP6 wDc7hwڇ}GB IʒZAV}p yq85t.xm UCp5w#N7LxfC3ѠieԢB vquШ/_+.]=ط_Zυe]%h&ccqx-rxA3ȭۊS&jp@NO\8RMTURGp[khPHY6 bN$vbz$Nl&{˰N (UK^WKI"ز_!dHOW&#yr`GybG )ۚDfo"C[ɵ['ql 9G[uT$~Js7&oՀѲdXS㬉3Qb~R,ٮm!Gqܱ EX'@ȫ_ EI$;(W҅&PV`ʝi ܾH\C@B$y"\3¶c9X%ᯂA1g܃ $=89[Ǡ;;FLeC#Q7r(fƽ vޜG]n B2%TR<yȭ夨'3SsCe8e" WbX$3#kY">:LH陠A3Yuu:~*cxA%=岜XUaɇ#\ަ3xRx* dQox1LM>x^sJ?3BN%2\ZN;Et+cnM;EUbGSALbsHx2bOE YI Hvn`g*/s0}x ~C39=oqg4AQa@<` ֗9J=QKtzm|YMӷ,9-9%_Q?l [oyi2Ƕ걋rW2aE-x}zk)tGU Xm\]UF}ha o~bJu.32C~sڍ]3e~wvh0H$"WBip -<: ǯB9BE( )lX-8>xlVKr*]Tw٤5ysgV:?d耣B^o45rj.LdIJQw]2jb Vh<8l2l}{-K{{{DLfYN/~Ln]Qn>8Zߡ'{XoHzҸlH.8|,>HVUqaØq4(@(?^k D~896.9-iZINOͧɤ;, }L~G q\Pl{Fr$3?5 D'?K3 (ț?LhK It?Fr#:/l 3X3;1X ;`8S't &UYNhkzF޸U0a5G~!+o$K#| V3Rqfjz5sOl8qr?sXAa#ZȫEјg&*U) rU'ZpW}N# 5Ē3P(MMZ/e4fw_ CT3<&/ )%6׀7H`K铛.EŪ91,#jɇEmBң=ca4)=x}b#mb F1e-ņ6&aw=V'8t^IV1թWy-.<9/74/+i\W+D)7Qk$ &ѻv,a;5?BzYD?[%qmdĔZJ7v8Eu`FpScN!O[(Z,{T*:9ŽTӃ(_>:T”D:MP>OIc{B>|<gmګOCNk=jM+ÛYoX:_ i/gI:߉*HjSЫ?p;5q'*'Mc%ȝMEi B4 %ьIo(=R}q;[e^ٿo`7$AYvс^osknyTMRSsUcW(]fF(X\rYeܱ!"#;%h/;~e{rzwnuyE[t}|]ݳKZW.bf=PyBTM( !45sA:r;]?[`81 5c"#'YJ|^ ?BLɻx&k{> &_WN.Q]悎:ZS=u O;x|[*(^`z+"KD1Qz|SɊv`i=ZgDJd /NY3fjE B:h6$RWjk(L08R=+6 J14tkz8,Bx\@OG=d;{\֙ + c^gf 6,1&4I,7rihs"/^ALA/&dCⳢ]46߉2DTq@8a gdrKb)|ER2PPlA=^og!,5 DH˒T)RPُ51ܜ%´t6hȄ,Dݫ# `8&o]VEJ8J7llLE)Vu T ofDeЬ8x+磼D6ױ m,@;]f/Tf9T{HJU>V[{쐅6.q'F09Ofum3ipVh6h1|PDJlpI^X w^SW7wE@x&GKE'̨4XIjF2ftKGh@R7_4VڀC?6 lO"[0:J# ,ͨ- fCɩMd{@ xu# Q]nE~8nb>jCsjL)a!C7zv9`nlًm48{0iTÚNT ۮ@:n<̺v|}٨AA0v n- b=*PJGqN>̯SCldX5S_ mƬoB%b;P Y/ BB1Γd>[tsr I݀țyi (Iڨ D03Gk`XeRu3*+1jͻq1فVG-\o hwھ=Z2S+gG߻Nx?J#bΧ=6ѓp;x"_|Z fxv,(\>|0|Ҙ('P ^;׼HrWBn4ݨZ/G:nΆcwYʶ~rRY.A8jF8,Xt:'н,ƟM oC}硛u>jNAɛ(|tI)-4R#}'HCԒT"B̗/]e?ʈRߏ~^-9`ϥ1)~r CF*e}N F /ӠBVүUދбTnbebq,UaJG#DD%;Pdgc 5A_.Cy2tU_>(E8nGuփt=pAHb C*T`n;. =6$(]‘g|,Q z~v+~\P5y䑎k}xh +[FݙGO&Edq!^[YƨB+V[ЏTt+ &5 ,| voU *r&i˛&rYra.6oT1ſ\bĥ 48Twd:FT2ݍAj8gh j1՞Ҙ`>|Eek Y+.f La 82udȝx^ՅݎR܁EpM>Ρ9ʿ:9 ƒPqqAsq\=ٹ>F-ICZ!: Na~ɓY␕BQ44vwm{v;rl2lpdjo9 \ъEgHhÍpl)?7Nw,.-0^2gk T/j6 ҄wW5Q>Yᦶ0gZ QGA VTyn#_rP;w7E$[R{A?6j֑ zoE%>"GCN7|>4=Mk)wZ.5òj7ޏvBfES]1'֣yMGP;KpzbK7_j׬и|͆@;#'$%=UX,z)TA Gp0 h#yuF%k׈?ydbGq67g٣Ϸ{U"m%c5KXvF‹t~VA8IĿqb2u R$#aIl G@B5rF)xF\v*!3mJs[E3i^])~W ](kV_}T3"OkQq膘i8zJ3EEA|$?iVfy WV ? -\0ëc"]pp뽖,3%O">,4 8Zmo)J'cAh&\ԋ%av.EY.ڋV/sIE3+Ƀ *Q;E@fsnYӻ{06-HyQg&vjn޵T.a7ze&W.xv+ 5 Aqfֿ2U$ZXO!By~-kʗ_۴LOV)!SgMҊ3 }$Td<*K.d05KP1YԘ1Z>[ dDHF~ЦD\Js)ڌ%9ԉ\Ad)6Plw!/(/;4@xikq2!5\˗œjK!uz bAMѧ(#<Aq7>n=`fh ދL8xfbXUB}_`;8,:7' `B/:~szi\0[Gv?uJB|>%U|PI|P@eiW_{~ 4Xx7άě]TWMB{ ~2Tg x #2c'f&:W(^GZq=Kua%$~xT3R~$iuS~=I~U)SBJ5Tߢ]0Ue_hPd`l|?B[pφ}Ѽ;(6JMXӻG&p)l~mWyeS'Uh3P9u8ϡ}{m)N^N0. tA~bK1i 3Wm8F&IR~Rn)WCFv5NfCQEFTU__ P<{?#{=@%!W(zF>*@3Gt\o~H}QC'<'zQԔFO~+?oaQ}ځAe>Jw1QZRSq c)862٨0~*L2GX)d^b)U5ߘfO#& >C7Z'PX9ݟ\T;70:;s!\c w~ '| s[S:D2|FDT.z|@yKbL-i<%Iay. ן0ǿP>LNWQuE}K% k3Wgm;6Dx3ɸ ,-ү㳤p=őpjF^Hh:"|R ^v9;4x'Ɵ d G9ȅIdP!Iħ סL7PoR3e&ˊ:iKp(!Hpo㳄xoФ}"v͓%~JT3zkUjD{H64sتk'NF>C V/%Ylpf,P@V:SS/oЋ8t|;=n)ɔj:gĭі(| w+/_頪WVTfKf[,lEI8im5@7๘>ZɄpQNZMHiuAL ]NǣW(o,S 7ZqFY/7jWx YDܑ EUTچ`Q_ ]xFryU9S*\Sj&FW'D~O_Ywnoai-]x 6.9r-*;)!$U F8ԍ&4 ~M9f:ǴD!ɋ rtYiL(*߿H,#93gdn&#6 :#hTT옢X.]~(+DK q~!][LŔZLk0铌m$W}N@B!;YeX @wQ:U E= ͨ>_BJEz{-ۋJv/[AeZN}#_,>K\ũ.WJM5eo^]A5¶JS:E`;A ]$]?ZfP_fo݊^q0/*ƿ?+ѯNͺme>v~_'=-1+0An5SCZ| %\rg") AJ Դ>Ohiq ns`:v_uH{{O>[P*Y= hL:WYg(H]I Ueq~&vѥƝ^_8z׬]q C`nCp`#7i>ʦ6Qɾ\uzusQ(W;},6R(8"&(pR><"l9?`t|;>w }j#kNnTБË#ow 7c %H_ىKÿ3.m{WypZ'=z?g6فaby&o;0r@s4l؅K;4R7\sŒ3)|Nq4On؅b 7焬O>&2]sكl݅C$ڃ|1¶7>An?_m9ީQ36wьRxwN'&p'u9c׭@mPIqHduq:9XjЮd !:v5S?:[D՞"uI^}2Z`yءKP GTrwKU (Ćs&+L[ѼIq8+ߋG (Un^ig%>3Nf|*WI3LUnӰR^2JW/9ipM GE9o,LE\1bX%fG/+߈**3g8a{lj^Mbjgƭݬi 4QGm*jɪWCS +3a֍8!-Fie~)b+UN)eVX8FG2ߺ]jUӡ51{'%[iS0jႅ(?le{*V  R6J+!)ًWxJapgdsg'deಅQV& 5Ͳ(xcܨ@=90=}FœQ FJc0U91e,ҿX7 MWirƝX79[D362ceX#ntScsȣQ~ӱu;7)^7OusJ0RJa-(|*"@mf$V=:i- -XECԫzjkC^wF(Oq6jJNzPAhukxD_ˡ=Sa}!IRcC?)T޲QaݰlV*$ }ar']Myy`͠ƈcZ!WDĥ6ر '\ Z..KU^W\&X -U41 u{IP! lQ#m9Pf6OUX]Z*op΁|F2T)%yΓ#TyA.t/4*lHϢ}\vNpuI.XD/V 6}z8pOa-S^i\aMx=u@k㡘!E~ gv.6_Bjb/ɬ* 5rAռ>Swȱ%y,7c@ǩ29X9Y4Ch g&(K3?CHT2ZxLH&/ጨ_-/vJP;vR[iJP͗)s˕Gx~"kj)e՞.zYMu)۰g^~v/l&./QV7)o_M\zlXe;:Fc1#vgYCT>u$'ڣ)v =Jt.{ i꨿q:ȝEoP,sr-I9[ylnJ+|GlL":Ðdu)`Q|.6QTu!}9={]D23LfBxO_qb.[Ano\"\ 0ۃAKKOtvÛp [1~ynM#9b"J #~s-m.8~ 6WFg4&ds P9dbkNԪzG8u=F1??lLӱ hV|pSXDf\[!6p(}ܰY_pM~ݻp&l˅G/]}:}#9tS&(:GI8VzX7g836׸rp&*|.\ע5ܭID3<%Q%Գ<@+˔Je?`C-ލևp W%`ʲ+Qn\& `M-;l|n 6vDυ=gEJalM^ PnK3b rfVl F ]dpti-16 MSk{M)ja?Bk|da k^Mݿu> k~b3l+u c,r~WGJcw>]Yհ(}\/vL-2+ d/+B$9_h*pLK:5UkzpBT6YZ8cXnj|L 絫63p o_U/N @b#OQLPiN1NO`dd=85&;pQKq^RvTK$)jNSXOSd%܋f~J^_1|C~3|~.%2h4sB;ph8*+Ke*Ȥ-| ׆ ¼GQ:<6'E6Tm=@t9HW1љ{pLYL.gxsHE q*=bpQt:eV {x5_g_Eɲzpn /z2g gR/RRh}/`}=/pu^uI=g-C=3`/qkG?ϖ1_:y*3г2VN׳{r_uq6Hpqgy4~+QֵADHZJ.~ rKޕԔPĺ~󒥜|JOg#nj_7vQ;zn/ lx#xE8Bʨi\|{woGӽ4 ġ 5!x4i?:?_0$ U.PmS ,³-[B\3r^eoZ*]YBǻ>G3X" FXm[7Y2*{-]mmm(rNG|d֋#-zxO0ٽ¬}>xzX a*ƺg/sf%4`Xa΀wbp zZqY˵G#,Kk߷j@p+C+GְC5X2}o.=.ewR⎱}, `SK[^q{8@0Wes[XM8(1L{ҴlP9'ln(YyѦFƎt]f->B?^:Ui숟VWLIdJPL3M Bupayen\VH@^}0g pp7L6hsr8dNS ݈͚X==v?ng5x%sO-sC}t"l^r)q4'M#zmbvSeιSv8m42,0_.OlA3`9ߋc}\y{qIЏע8gĖ>kmUg Z/sLq_ 䘦e"zmJ%f0YhSԿAh˴6Xp D#Nb*1 ":N3ņToß6 }tY(9-ݵY(ޙenڙRlȯd㙛NSKn*ǧ L eSTmVHC7 vqV@/~G5gL<¯LhG 4z^x"x  /7 rչ@W_M5O=8 ҌYB Ch*Ir5=(/ڧ?ctp1q[q]݈92 ,WpA;Q"X8UZǏ ]} X>B.x7{g!&L8f~ع]`b0;pL<)=luF,oK;8q;Upb86*Eֵb%;eolaAʡ%'I+WVK9?Ϙ-Dʂn׎ s  5gkei|ljGHN2&}|'-IQxA,CBa8^e$Co {#^RdnU Wbj2ɂ Ʃz _@nG(%IGq`oih"_JBC.tҞe&\y\ ,{zM~YD=W7+IwF~1NCu( w ŽAħ x:+8@H̖Ȇ6@Jr 3l"qZQh*ÍBv$lиGxT}\)P LV}\ N iBtzc fh:O(Gv RD>;'>_Q2KVHSaWE+7>B/;|r!BV_L=bR!_ߵj ^XCrtvx vwׄ3#d]s-:kc'I1w[FJJY{̍f< [$ Nai{"_>fסdUfDXгj}=DcO/tʨٷ1HD*a+f AץB\{3;6 5 sỗ >j i6VKliUvψÿ@%BIa`eBi6YJ\[\Jq.”k>E%œY.B6y%O/rt_~=2&dY.ɭMʾ~C~^D$UT3k.70el{B5|T]xP^ו0`v56cwH?(TF^%@{FJ=0:pW`'IPܶ6,1R})g^cz<B?&m|F()M9F>g#. Y)Pq(>?k]zR =r]K /C#@vOGz׮=HԝtAd+J6TMM#c)ǪIGLXm39,\L}zaV"Q!Kz%MU~/O] }Sx/CƙHvЦ6L|̫A $%j Ѻ]AFnJ-"F"j5;eVKg0Kߚ8oNx"iM7@5sRu[B|9BxB u7R#` uEP ߁Lm,CwGaGal0Dx[Sd 5Lҩy 9%: @BBf|IeAf+6Fo0[,ɩPZPZZPZZJ*$$*)$MUjO>(&9=s{Y0@w0vx{eC= 7͝r,wei>S<Ɍ[ "x& 7 - L  -LM,7"TZkX_Z_X:d|L.#8MOO@)-HI,I:yKO67 I#԰CY2̿+fj\ s dћd[ Zg T+9: jJIjE^nq:>g͇^ɹ5j{jR3rzNrP#K=0bEx/6Ml f"执y6f aLgx340031Q,(J)-`xp#"{U~RmOA+ J22Kj*ܳ#M7JJ RKR r$cӋs+Mb=N*SxMo1fUdRU(P.c{V6⏔^e3fZrQ-n ~*fHȔV\p4n:4 +4Aʂ5fT!ϗwm9a@2nfMjp{;L;åw9@1H ' V3 F ;_bCyk,p$\@(ǶL5`쥩; :`EbSwS0ް!]),󏟟Fs~cx@i H#a",#\uP dLԭ8dI?0pqjUJ]c3V}5ecԲQyzێOB LsSd89v$"a' fre7`^v&_n׆g!X3(=(quE}t}0S4$ۏU{nAwa~\͡NEqʽ OG>3riL [xxʵkdfAQJinA 2BPi^BIFPtvaq9e mɉ@&1ͧ16,#PxSMs0W<O0-pÁri{P$%Ė$J8$$iow_4{WXWhf>93$WdAs.q_r,;T b:kn`-v0H%Rx=\ixk%|7XCzY#8k4`$hfj7S4`A|5IZl`a9b5+}aYZ% -pa8BJb^-BL1u's9<'0RIR[B2bK߇, %F@~;&d'pgg6ƔkIVA<4&N-Lȶ=`EꓢUESǒUe7ar \%Xͷ5$=O uuCuy)`)GOS"`b(=o<~ɍP޹>O~yiL]_kf?x}RMs0W<O0إpÁq)I5M$@GTǡP_<]6RUj; Uy%tg=Nu핅<;j 5 c-nV67oqQ].,3wR934_= wXP5+.4La֡7:{:-$E^ :j%I^%4V e9bRmQƺSt˷SeJb%~=x֡:PS4;{|S`F=#Ѳ3@92s'Cǖ3qӔOQ+P"j9NMK4愣b³ltIqRNYg(=d09jyzFWAY~C3>opN1 &Fx}afAQiAJbIj 2BPi^BIF\\$3'R!-$:1ڍD- fArj`ڝDoʸ,P&e&x:abfsQ3#}#}#C3+C#+cX&;sp*NNfk.y9 r2K47Kr0y*h嗖eZo!h07f]ӌ B0L1J-S=❝]}\b@%E% ũyũ)%̼dƾTb3$@ ᆱ ɹ) -!jaZ.c$K¸%g3dFnbqAEZXK^ .rW*c3Kl904;')|34JÍizLjR[.|c]!PQ7NYA,,pz (Q2 KʱeNj׌6T00=E-FlrQ)i$HS"Vi@}l\\/;$VXI<=G~3oSmv2qq1#lW{2K/so/ >6{]pε!"`y?|7KS"Vl#aӃ]pM.y&(feBґՃ6:2j#['kA[7+"kȒn8 ;&GZa˫Ә܄bZd#!}k#*G@K&QeB+ iPʚ;EZ홺boǷK5v3ȵŐD`\u67>E3.]h{~vGFڙɿP:PL ȡW98ՂPPJ:8FeO{8B$Lqʰ̋2xeKLQ3}A:|`ˍ4k SGt^:S((tчM턉BK;tRx Z]j`ڨb {*BE/&XyQEt:kH䢨qFa9Eq}h"vt{P(* ȏE^)Sȡl uwMx*^7$Ҽ1g&@op-Vxwy~+n)`tP 2qSQ( 4|:3!*vvQ젠IjUN`7 V82X*H,]+ī\s9"9i^!o% 3>YLZ 5jt >/e3dgX{38X6p9,gϢJ{ta ͑8TcWJ\u<١&i߲b.ʡJK9[wG1wI||* sIx>?flgœ(>bV 7? x¶ȭ6)jBHLm0 &)CU_R'--SKxy]/C3 h",sfADpu KI+-HI,I Ppq v JN\zhf=LvȐɲ,F&aὰxYmSHlwae0v" U,v.Lp-R idEt3߯G%aS:G3=O?nn}w .d>ojȍ<i9 @&OcvG,:3p>80w\ױp/]6 BJ-pyΚpOJ u:}w$YU'R-C5|r1NA !0Zګ^; JQ8PGNdWyj)tۿv;{Crwx9~ ~2tkJxIaN?Mʴ\'YCOyof=n'zEd,I(e ¯0?RhS/}_eQm7p=1[_ՕN(ybo{?S Rcvš`&cvcGy;ܵ _"J&qZ [CC3Q,’bV|d#44sBc;t&>HfJ[EQ-hUnf]'-Zs2ul~6KyefH2&%}{`67]ZM$U!mP.e虠j(1v~rAhd] jCDaHr E֍̠+YT=E``{ˋz+t^EC*h6PRbƢ$,Px)RX:fp?)8ZǾD,˛V1Zj8l|:?8&m: ǼER%<7n !0Ή$ TMBH'FGA+C?2ڸFω} H"AZAz V`{հP'JcU Οvbp6 t~ּ%c(MD>p;WM!> }9| Ėz<{^Hfay97ݬDNĝZeSR 8E!oAN: &UY_*9z-'#SIx-\{jʧw&Edn~β&hEȴ_pF"M8Uqmu15Akq#(h(O72R87GXo c13w[L)A^웳Ao_"0,1 ۻ_9ScK?[X r TW&7\Ir˩YN8ig-Y6T]C?W,4xC,QC)|H4N8ICLY>Z"./V,D dK.H;N`nSԉߓqnhhp(5 mwٸbypQox}QwE{,-叺- f,Cwۮrڜ;-u/s[0q-VU"~G\Lf.a׋YE h਑f[8޽Cθ-E:nec%[ftQ E$9iqj 8eYpsNs Kr$1&͚" JxV#;;(rr6DV֫kJ:}y5&ebuuVvgrHErgrJZh #r0_bo#}7aByt*q {/IE!.UC}{z_911V(H.&T;Ȝt 8ٔr6y o%_b3r8QaO4^ܻ;ό,jS,}oίz KsqVКRaWn\Wib_>\K&x]Qϋ@ݚnֲS֮M?= Qlg&iYAA|g/E>i0>m0`:CM[W6w {gkǤY<_:Sq  wfT0\=+*i4T`CT*P/o%˻9iŸTg UF5kJjccF~ I]N0i=$~zUTCd_05rfU _kƾ-x340031Q,(*-)+.HMf0,XT7$Y ok+1$x1!PP@π+(5'58J'395q{5#KQ^dC&c#Ii~&zG?3/D$rqp'e'gd'e+&(ERZE]Jj lƀ'MbZR:Y5OSSKѥ}_2ixrߒӁ(&o6`S\ri@E榦 EpHf`o^na4QgWE{g#LLҹӡ]WO5Lb?bE ˏrƭw5kG,G)-HI,I:yKO67 I#԰CY2C=nW3w\7jd[ Zg T+9: jJIjE^nq:>g͇^ɹ5j{jR3rzNrP#K=0lx/6]l f6x340031Q,(J)-`xp#"{U~RmOA+ JJ RKR r$cӋs+Mb=9iSxMOAS[n h -b j-!$Yu6bo^fcInI=x0ďA?'m+ޘd'g癙])8wsd@a[PF ]q2-:4A\SfҶ^Sy YEG HEY >?ڿߞ7/,_T~"PG ]uujc'G40T{ld E(h^@g4EiZ&r[\&wGK˨:xU7T32NiTa5^KaI" ؉tU}&;R8:p%(URi%aOHYW2nXk[,jku:ʵqgav/Omж=Ao_mO4'\*R5.^rb&R4ĕy.#+:ubvAE|Z%c1h`wIK|'u}bWCH&!,DѲ%+1sbW]dUKN9{n`n"+, [m:m [ )jo xsi8_?d<.rNCDBU4V:s@WL\qӃCPxtPp h0nFq'WDѐ[H1@Ounu֒}&R+&EIxb%(H+Sg,9a@IfpCz)Fp*fDͷ 5;R'MD!M+ϩȡsSs26'#f8>LMI~~~Gw~0< )Hjd(V2ݠZFro= ݜTfmEyB.!UO `\T+͞Uoa4y/JEVS4"4$H±ABG.U\5IaX$KH} /BuI9%[2x~MM/C3 %7@BAɫ4RHK/Cו+h!P( jf1MfM>h r0s1MXxYys6_UmҖ#N׌ckdu5%$%($=KrY$#S .<ޡWvW5cs/,h"օ bt@YDڑR RI ׮7'd/HnHf,y"a#BˆI?2p*I c#h{2"iEI"|q{^vQ{FeCl| iDK<:5߳AcDd{gg8иʗwo_}+Gd~)=a$Q+G'[DbtmmWxXX3L֩mxV wmY?b"GV3fkWM4#]71c' 8W{ H_j8@ ϓ0ڔ`͸n=[J'")BF*DִrC:ES?em@Π[4-C KaK/j#GӆSyz4X(SW-9Gߒd H+[{?ulx |w //m;C$ `P[),TcGv'1:0ʮ81C~Bf rqIYџk7ZvOXV&h'+ 'ުp o 0o'HDl{x W\I:} aR\hK 0F\9R-?6%x%A{2є0< Y_m6(Ӭ'ȄUm߬)|WAhw7N174ix_DO2^pE*۩٩B\RdRA8)BCœߟUaO Wݥ)ȻU]xhlmR,L e1ĐLTI9*RHjݦ9z9mgJC5l %KgQ;uaRy+C\R}a4n{_}bTW_F*l.0OǤtUU$04HYݐ6;"4*F͎7GEM$KJ=YF "/iШƘ?4P]M7U_A1kSei"믻{ Hlvxu+,#xuBg@< 9Qb#F FF VV& IYٙy醓2EtMayLE1OdA,% zIF&[#H)-RP-VlͱQ*_PW R&SZT NNg w6y-%opq0)sfx%i%ɑJ{& J%Ls M97ۅ\%܌,SXLkheE4ATH 'y>U“B=Z R$+f^XG$~,uKdld0#ΓeSU9#B;THrrTVkdy&Is/'fT"|I'0CLNPl1lP~s\?z(Ax[yb&9c#}C}C H!71/%8ߢ)%9 ZE `<[Ĝd C̪4⒢/L n² {;( 8/2K ׵K*MKK-6R@E%i`tjڼPnjӜp%}p`Fx{|yb&Y#}# R042125VHK7|I\5$?#g1nFsgq(Tps$L~sT-;SxmxY}pTM0Kk` B&H$ !FކLW:muZkkoEDutpLkt@Z?:HK܏ݍ{ι9^.K3P'Q=wJrB6PBDm ˘LJG59eȪ &J+U IQ SRTQ0"4lB?f]`El*"D=͕Еޘ班G  HMH+4bY^v6`p} PۦMu: +B݀FQv[z{6߀;}> O|[7<ݻ_6*JPR ) 4yBDC܄j7+^_$#)5z^hnUiL] ûپ1UID4Ik uev w e%Vg ARoOM֚9a㹁}5)}~lGfĤHh֛"ᝑaڊP ==555OPJRG1) 1QC (N~U@ĘhܟAt7d49)H:j[? !,hDЍuCIF[̏M(GMn=@n\ z[8"[4Ld=n)Kt18:QK!zԈ|\$!S`J.lP^HnMK TQF)a(E%hGIBざ69PPZ2mu8G Ϸ-`ߘm֌RsLѝ;yw~^i : "z:tݏBd$D0KUC"U5菫 cE( dD#IQɈHJSR+xfExxX(!c;zzPO F"55C&OעDĦLu( 5؉Td$=qI۲̉)I, ߲W#q`xq*j(wLp0PM#/L< 4?Dy2a")”GY=0 8$SףguhY|BK9z,o5g0lrghB%mӇACcɂEQMCuuHs\L=`@ t@(l" &&I+r&.zN蠥8R2e&LYwe9zi9GiE|.V` *М46# Hl#6&NNR HC$HI)K~ˇ%5.p&.t1M޼Y^乼35s9_d S Y {3êlsNYrĘJB;xp_O7;We U΢S.fK9 ){[tDfv̴7 Iv(:gGNqb̏\Ea ̛ NBS(3iDnFqnvwsb@r,mN(7OmT`vsgH``鷴S8=ӵ׍x@z fD/5Zk1P^@d2?V],Ijzj;DdkO!9mg`Eg(8cW\2@kV)1PB ؉-6+!Ԟ%v%nAQax܋DɯWBU+ ngnlY>8n^ JT)c:;66!x};@yn޴0'y:Y5T[wݖ4ܶ,܎ڻo 2ح[ q5F%Gơp8wgǎwc?Okl٣Ӆ}I1 Bqe+F'|i+'.Q/)?᮹|tfv*qi~i ܎O$ϟ q[Xo=NIo YxgRV!?Z%pSg7u%gOUu/vAt((6J6!ˇ;؁~h!iL3&rh|ىo̿+oQj@\sJablX+.STMM9\ ʬEpȉ\`/Ԝ7o2w$ql#h+OGvBͭb47jm*IqdG~FHx hj3aV"w13%>~LBv̯]ˡF5IGٿl~-.?~/{=7?]=KgjQU c ]ǟȕ}°U-_ C5~\5J;Q|XG~-&|~JF77f>uP&DBAT5mf'rHc0ÝvM%-_d-f*|;7\쉿>ɞ]/%m8"}4/115@X+x>s 'Q)u %̥@;/yl2kgO\B>CA,#l)q^BW\֒DM 62`Bt` !u07 )ZH]$#}YN* ^<4L^@ҝH#@.&?q[eR}UZ9m:wnju/D|'gKuUR=+uY@6>.m] :0_9ato wGonTlw$/ ٗuU4%h;%>͓eHo+8^_{`th"` TG1}VdU:@Q, ="D&~\ŸrB,|pU]G</*98ܤԘ2MC65\^ƥ8($-#iʨ>OC?ɯ;}GH .CG"GqF9ú{P&2NHXnv9{$H8CPn'uГD"9N<:DoB>m-cɩXzo"8q].N Ƨb3L"#ᬜXW&3RbT"FDP* J!a ZHt^)# yY̓3W "(@^Xt!%(6D+RKJ yLtReNF! mdrtİ@,xuDԏeKT!R*J4YVQ{4V?d/ WN$/ ba㪰 nb2!K{f% ,iRe5E'{|g&2%k3+ZК 7k>|ki.~qӼH=_D6@݄!s23]AT@kE$M6cmypռ9t/VX\P5g䣸QHK9!?pq %3Lvv78q樢HrDINT-妴C;?`G(PMY-- ,˱ܻ9[d' ꧱4̼s<׷urzt'$ߴO<W)?I*5cp4JA;LK9y̼ =z"GA_(!YЅcovxJOꊗⱤ/:4O?^HKR',=^./*i öPY >F#g0J+T B>`]vИyR^#vޖX#pNMNZZ ˭Ɓk ł[h&cRcOp>"UC!k+Z,R*T,AWؗ<I (ПKg`$]!_ ]GiHZ DH84bGQ~.BQo# MֽHN#1,lǯ4kwBApܼM0tJ>|4<76;Ii=J$i2"EEbN6͐i] @2t"8xD07 u|(͇#!<=dI}%YJ,e2o]#d_2o1W_q*B[? .@i]៞6Mhum)Gp4;v61>9q'G D2iqH,)GCqk8kqzjϢ>,fT@N8 瘜uAc%rL؟<ǒ %x(j'R})mM돇(T4h='v.OM,U=4mm /z#al+ϖ>$Jr/EX|&'A_//B$~jYy1|y_N%꾜:Op,jwav,e)Nqa'!+o>|l#lI=S={gi't]8ԘWr6ҴP$yG{~1ycPn<[J&oyqc#)a91D~ ud8 A7;y'?[ ɷH IfcF_hV]a5e^y{ '_Hu=́xh)J £gȺ` YXǢ HHrnh6y)BM7s 2?8:,Gɠ#Ou34JRG;=$qGÉ  ~: aN N\_懗qPR`>"[|M.->*sv3t!欩$ VC,dhqi`zB2 [,]YDP3+ih߆o_O/fv[𼟬YWHzRWΫU=ԟB" }\NqdMDT# ;JV)u.|xAȴ|s&9&}i@)*yMlS~ɾPrT? pcdAّKK..O'M/6?O[w1_\ Ѯ/uWė;ڮz/ÇSu'V÷7N݉5{6'p ,ǽNjK?O_Lsʜ*d JE@dyO+ltSyv(攤r]k˺i/x;?{DOaCj=]k Cvz̬K<1m7Ū^59ak+T5',gdK&CN>'R{ST'̉?gbQZ{Wwz- ("p2FJ,)Zć% SM%x8a_nBt#1"ťHPOX0de$ D,-K9>SY^bFQj4V^L|\ķ5,"Y2 5-r8_.m7n)ͮZJ3NI,>(e'DBh<bwzs_̳+)+4ʴE*SIIQyM:-dP7˿6>ʜt E&fMBJ[ ȋ 1Xq-jƴ$lJ֨gJ@f ֳсM=Nܠu.u*s[uZi(zc 4\XYk_ΕO"Gq-AUUX@J)غ<'H]!0d^o/B-7 &tpU }8BbD9ts_SJ$,KH(; $pN"@e,$D q?7$Y6Ex}g2=ݻoG2AZ̐ᏞP 0C?@$`,Jg1LaJK:DV3Y (zKKa*n nm0`u.TMR?u] ! !UNZ=ĚeNeNʐUE2bq]xh`̅LbbT9KhI, ɅI'n=Y hRVe"iO9n]pJ"d"ТT$3f =#&M7mYV-HL+[ezB!ᴢ_Y9a{H=AM U=N{F&q7~tPL Tj^u:4a>3xU-CDoڼ_`uW< gfBxPdN!;"P O U9v4:l+Skb6tj;I@bF3[*`ɬݤ ڵL!M-fZ 2on5SzG7K/9<͛˴-oa}FlmwTH+8(TDҟ\N={G;Bercx<+Ւ[פdՊAx.Y)@ɸM`z H<CHtQÌT1zi[)Yo{uùJpUŭs+RH`K 5TDkF;Н9MP`lt@[ˣCVey X%zbLzAiՂnؕeuȺ겣 6)R`X@"a@oht~ a|= __'7 &Gۜfh5NX|_NȹT氱mW:݇QZAhmkЗW ;wԙ)BWOл's>41}kfcqD,.6l1v*p3NP\e_uՔW.aG$X̩$j"%e".rEtԽ9Nmi L=r`t8Jz*~gqO UL3 5]u<s%O+dEy$;ӻn(*-l׺q&tvjH%Hũ7yu$q, '?r&!n$t=!Sd:j+rf3ϊHЉ}qA^Րq Jpxn. ^z6($Rs3{5*:3ìDviҏ֨Oޝb&Et^V }j$3- Hd9~oqZ{_UStݼ52ɟ}k=Ո=s3>R! ݷ?g&wa''΋:i2N ݏʊuy:/RLm@]L);fa:36t*CС~ԹnwH]B_yܔ_{͡AbH{V )4rpwM (p#;d5_uvb *b]ƑC.29M!ZksXfHeJ:fæ4n !bkKk7Oz=AGBwbqH5v(3-k{tbH7X5orFA/(kT V)$}_n LR$MwOJ*HK.]if6|:%c$7f}v * meQ 4<{[Uh@w*qu*5鹢#rPyM)S!sB112NQ} Ob:71.jlR2r7Ws @Pի ד6 e(~sՉ|DMo(b\@o/fө\*8,zqGŇT|LR( r%o.a6/ j.uSa+ր^62" 'c0o6<~;Vfc>q'{ df>xdr]UUѓ5'8%_z=. TF s- ijlԕ=ҍuFR:bifkmE]gGmp [D EBCinՅTQ<^enѳnp, u%5=6N={wFkׂ6t|6kJhtEPlCiSc,hdb FNLOё1 "4OS HE PJ!%aXƷy4uB;NX P EjOuiB(ZpfJ\]BO&Z)ǎCW2jpяA5M&HD\6D"KOpwʽ!џt{P{[o5W5$JN2hP"[>8t/\5&BiNdC@=YonT&Y4[܌P.8nPܾ>T^,*mKH8IH* ̚KAJO$!HXg3CǞI=2:eª% ?UИ?grjw-*VJBQRj]ݺMF)ޤA*-!=m|4W׫,PD$1!;TT^Rwz [h]^kfiRkeK˘dThw[E{/͘y _5u/G> J_8 {tIV(u='A S/[Fj{ZV3\T P}nEnC=FSTBh0kZ >z>Zs΋QBKSOxD :r 5sN8bO6Acazى5=9Y uIsWӷiTt 9c5k5B}.P&oCowZdxN)莔tAįsrjV3,N`O; %SVt a _L,/-PIAp-Q:m*[H8RUH؛#v6hu-r>9@p턏"(v =@44I,mw׍(JkU;Q=], mcDiZGDa>T9214Z:[=-AfT\-IuꧪAR1mymg m$6?ϴoW P]m9=<2|ѕFؔT7z 5Mb #K Nuւ'4.35پkٍZd>TQРƠg1 #O%ѝըZDZUx4_x-P{$ѾQ<'GEgVBQEo1uP1 4 zHŧ`tԂh{I=8=0b^6l 9n$ #~dH)2^%nz5DBq:&3 gi @Gl#M[tXyĤ1nCNoP/ Icb+zۛ$ GC] 8??'G@*Cie %աhMl +cL{O8^7] $Ԭ!C$46P25d wVrMarMO@=3Z #eZzҿcr9jX2I% r}(* c&Th+ַ+:YpDQ,LNb@]E(WN|15h׷rDЇ| t> L^.;JWg[ M7sՙƯc{G&&-dL?=Lu%5xw9(k#־Ij 5N.c:7it w+@³P=h@{6w" jm텼1kUkj.g7> k"v+GF)EV6}гugп_S z= ꍢ.8EaKPk($1y1t7.ZuBԛ{}Z%X|ځ_(59 =MGf GO2' b9)LpF%C9={|S&fUfjtP#FRze;{+cr'?ՔUIE ϗ/YOn{n*!Mr֨[҂."QskGEpz=EnҚV%G`>Pت͏7=rS%s ;| }ev蝟*NNßLj-n~v8ycTɊY !\P_&AacaNF:FsAi0O1E"4Qfg3*7Wp?Tq2s)5R~B I;ֻd1QrҲmpA%4aEjd2ʜˣ H˘Bw =ҫURn)7}ʸT/dXzEC]mkjJdKKĘodvvStV,*W*3 ,w(,۞sRK3#DaR$c#GUQbKh+5#_)y%= iݤ^U̒<M+گW^ 5JH_%"t c_-DO竅HLBj;W7 chAtc.t1"[OƣEKJl1{ g#G`T]հsXw)RRԥ*2o6&Td;Rgw$Z-"6>]X,: ^ ix]sNŸҠdZE} >j5Ԅs:HR8_ PKAi$|uuYFY柙֔ `'RVBo)Yu/=9Lg~[FB7rJ>W7xiySw&9ˎ'f.(5uٸPwfr ـdza<%⃠5Q{;DlY]wPG |{gM^=޶pfuG>)/QfF$Č^l z9Z8%jhC/:Qb B3/mjfV4EJU ׻ E7*ˍ+z늭 QSr85\QhVV+Sv"ʼgtv||xU3g"5 $.*Zu>)*mΰ>n1,\:޴"ihI/D uO wUOۍUyp:iV"Lk3tHyWD\7@` snNo6E (,6mU0ؒX$+ k9Ϳ:B`;Jdf?3b5$S A6߭7LN3ΜUprd%_2C$oYUҡOEpD$F$Gj]5JDs ]h<[zx;^QG3>{ x-mv<%ȅL㐉-iUEr.͓ng5s62$лg+@#;~;)R,2 8/#˄\ ғG?^jT rOǕ\=p=snDuDIyڍr,z! 7FIL{jno*V ,M1YBiw:t![~j9°Y,9®2j'{oMxg.w%Ւo|;+\Y'FMYZ%fи?b7R3ȱQ3|r]WBU+zvR#ڠCu.I ipViXn̺} k!X,YXGgA#ij%^:Ω z=\^ h,D=d[tY*Fo[,YJ#i.͹^w=yBN\|4̚(E-R[Z w .$*-)CfavPY04bX¥2ߟS˩ F@pX2ͰRs!sEg UڰЙXlg1`SG=J ۝xUnAϨWo,>}Pu*qs,0O+( $k 'M&eRP^>Y1`r%HLYiץ~Vd蠒ϙ%5z.'Rqo58@33| eBk'"v9X_)W } B-#]m:q@E_H:+V~ $Bz;6Љ7IiSXQ}z-h-&]pHA `8Q4 #63A5 mFEtYSOs>=OZ%ƪ _x#B+i!G(cE~/~9R$ o" }-Q KVn#'2Uy(b(5N#C28׈3xvb9Vx%GVG2BWR(yBkKal_WlhJq{2LFf^j lCFV&r+OM?nRmwC &תFbc`1kd_FM5}aLFjƄ2;|e#͔Erpr**l (46=2Z@Adɒ YBvnjMӻR&Y.VEx 56`~FX_^iFV*^7 )Cʦ--i&lFG&c;})LV/w'[CYtfX%c>\&eV7;y (ؚ߳?U8qsȝDe*&A1p)ñ=l}Rxt;T׀3=AL%)%6ېg 0؄J|FfϪOqi*\-٨&[_FW68^k?䶻2zvJfeVZ`4*~zO?nnwP_ ;gC_75ۨ20hX{sȣ&QϚXtV 3B-O4 U B14o2f U?"x:}[٬ݹxEhYY@ cޑ*_j\L]i7í&ѸVũ(D2a.SžuB.|JT~>.sd[,Kq; Pp=*t Gq!Ò+ Lrb$&PY:ޤ}QE'#^ [zo)l_r""%9zz>V8oxLhs8'TbDCSW0|:aЪRFa]߾Qx#^ aРЎA*oTsP* FgH(J(9W6h{y'~uz?w .6I.-gLM|&[!],ˢ$a /PT\͸tN=زE #~FbԺF:-.aN)9v -%,mK8fEƥx)cuf\4ۅml~i"I69\CTMGv顝 K/ Q/IOh&JX?xzV)(V.[.I|ӜP0FTh3_~E{ZCV4=Uj .I[нϰ*Ü~DV^mj滄 )E;f)^X}er+ Peo'bfr!YkW=_?_mʰI'mױK׺.e#Bo\#L[F]CT}>*\yӬ`ӌNBx6 w&$"cY"ڛ rL-T cV;eojbMpm=u=B>$ufݐo a( ?yJ萷:j-t>&Py 㮃*&(>W M)SU⢓e_[[X)麮%~IrԅB ;6UC7pZ23O>Pw%TOY$W%_5Hк"y=0dHlp޸xҀ9 `EG.z@#a>4M_A!S\p^)-L|b ZltBbSezf/+BQɖ|hW#[rO.GIU[wcvӻOn`ۡG|e?,UUF|4r9Bmy6 ¾{A#,,<ڸ[D Gx[3ѝ4 _h:4\Fֹ1c%[8.9[jUW.GZLni8OtRO9D"A07";8ENBݘ()ij++6姶|rFO%{vى.|#"ֈ8-"A ds+_C4JQ}Kd֣I Rir 4Z*{Ʒp fz ]8V\E$,;AW5@fQH/aND.Iv1N?sK8(IE/gYCkL'璼^an;Cx `WVZ?WF{xKPE'-bB-ۛ@)N_nC +ߏC\n%Tbn$Y| *W>\K׀q]6g]͠iMwW^VS> kfw0n+aBVoZۺU10Ȗ/MI7#ƖC$rmuB͒µ:;ǻ_#su$!K*W4#̀~WYDK;!OJJ/5ެ&;ȭ'@⇒BK^x=?׷mrd/m` :r22JH'uUD:n[ڠea~PŠ(/3Owԉɹt#uP;b5`9pG28e3Aۊ T5EXcn_jrȸY尹0YWB q}OQ*kme݆o+P XatW+R?8(q]fNs*7"IWU{0¡U(H}XA휨#!<2=}3=/Tr"}JlF{Ѽ |0M믇6y@,}֞KGp/3PfѸL)$GnwsPV%ZbW)'G7kqp2P!e5NXAKWٺzŃyjPݫHLB9y T@"a])3_ZЯ2>;y 0-pS _ݯv {T^;VCR?T Z@*q&j=PZDt]@!+yЁU(Sor5z ^KS9., qJ6j3%)5*$^hR hD1C7UL. l^2z z SF4!&B06?ϖ=NɅ0 ~H ѣl돚±HHI@wNS9 |`(ƪU<.H?h*TIQ|b2કXeR簈P7X;*@+'\Qr0U $Iˌ:Wkdt`~dx}S=F{._j3By%ڲM h0h7BMo!&\*TWX@%$d HIJwu_pJbՙmΪ V,His;DV=IE_gJ{|M M`Xts^{'!&$QJKN v{Z&R "py .cD*2:71|18#KRj<1ۗK"=Qʩ~ I)Tg_h +k;J;%zml)M.yiIThX3:y I) ./R^1U !"[ecnKƊ5 n|jj^CdR^n|flox렖9 *iȽ!jx_!̪,k3Fȼ#0\)3QOqh7J$rB'6K]iݘ}C oxGh0BGXLzmDXU6"{z,L*Z iE-L2pŶ>0-n C @ٚ3?԰KnJTmP"| 'I7ǷTy@)?Od +:m4Mh-L;tB l3Ossvf?L/e@'G '3%J!گRZtIL~ԫp3o9 }=cJ1eLUY3靪0{~O \ ]!ےiVy%7L}5QTa?5=XM/Ԥ)љO\8_:#kЎ6oޯ'7m3 -UĮ kmY4S= Ԑ?%W`|IC 1UDQ FrR*B10#{9rv$#ԶtM2 b|̍;)!G_澍`;u9rS-dx ؁uLyQPU&Br]z++EHoCO׃&1exȴXN,";p%2q#Vu@IGV}loVѝk`k59POr1i). 2?[V J󝓼{o_\:גop(7@if~ *{ JYsU^jU vz*=5s^3Eh.?re%!xo4@sE"OH*קۍΎOQ]i_61a4" IwH,QdZᒱkk240J])F7@(#1(1+x@2f+(b+n&ol+r]ޕn=?3?" oKsܢA?y QKe U*Z֗#!bD*ZEnaUYKQS7 }+_26szu+l~mѧZo:)4e*ecFޢf89ⴉZ]QR ;G5L~vP0;rKEJ!]_^PE#Z贕a!^{/DT4dָ ud |4YkgE5)TwY?'+jS]昊qG#Oml"x}"B6>\?N;/mA\(RK 3>:92i:JQqͦ}%|:|rejN|kiŽ:_)4A+`s𵯘sqW)ǟUu41_ʐsU\L[-*(1D[%(BA(f1vTmgVluT8_PS]H11*!\؋1w3,V .-WSAuŃ4>SEN+`sjɓ.rOD I(_)bMFg$jC#ژKrԮFSw&%>W RBrP$ȕyhPbZ4P|%/39ܛN5E,lUAnIK8r( C5g+Qg=x귦*U T})pi3U='S 8rU;,eTM <:Z?]N2S!E hzQ`P2"2f^_l)ލ?G`C{KsŅWAcJs6#USdTc'+QpS3A3¸L˂mJ.-TR ִMn*1Yp1^6p~Y8ARi8a^4- &M휚7EbDWFrd?xkBIܣgXKbdO52`X%tY`-&cփQF*Z_H2|Eaad 23 !5X# kdFf&cy㯁b^qy-D3Ԯ_jg'h&{pB8G%(?q+k UyF\`Ĩ*}l/R2߳IرQ^@!7uEY3~2TTHT;ni6LF=swl1\cډLT9+jgf5?XLD.1% kLbz*IQgrགxh &#iI3DZyv?kJyCެ]qlI8U*h4vxa n)*?I&Q<ðGRÿ4C~ɎM9x24M"ܗUpJ_iUJ+t tk md[#xch:i%ŭ A=4oՇ@!~P6K5nXKUF270hΓ+zSD@I m"5OgJ@SO0+#faxw sU곋e%*B cG(7 m"tIKմ`v{҆k:KǮ:=fԩ?C.Nt,\_ hk&ctf[~u#`-.ZEvjLeU$)kT·@ԆĒ?6|rMiT,ӛ-nҫ3+H'^33gId -z`ݏ5(1o-J鵟ٮ̑HuCln)Uzcx}['Gw`6 $¥|TN|f ,7/_7Ϡ/0UrngJFjU5PlWl߃}aAӶ}><`SQ'k('TD#hE'CVE`Mgj3o -\ "<0\19pI: ]*lI.dDЪS/l1 Q;uA3Ѩ_|K?M^OɅ 8?䟫~2x?U%\r sK:d>;wl1/ Z|AڠAzg^N~H.L;kPEݢ~s%tJ ;'Ȕ5\k5Ͽl(+I{ꙞybU&<5 ꩕—$_ȺZ"V2̝jAahiZ46\U`EdAH!45B|>s=l {\̺25BOoC L O-N}Tt-xa'؆4 jz1R\uꢭGYT{Ɠ4]yտߴn?c2cc8h/rxu:1521̀^5"L_l3`9L_y-x@vQtɆV7 C%H*uֹ=cpCԽs= )5.;6UjaR] )Wh*_ P> \!B +]Fsŷ|dǟ9F}^>+" r= _C99 ԕ;*zy,K=_< L$=8;HHB D(J$ZCd PEZre4_9in{86:ñ4mƎk#q4k''@PLgwvvgfw柆6AwPlv< 9gɚӎV0(63sJj7"8vyK[4 ƌa)k6+"l~oo++f\x%lYN=gf7j*GǬbꏞTi̡jD싲%o֒Y`](?ZؔRh[h _47L7.V!ljvgF0^z `IA v; yM@¬)${% 9}",f9+Bt@޾%P*ψ&f̌YfvZKmb~,"ju:@Yx3zXvDŽVNyO4U^+ g=b<~V,i@.5:A)YYzB\֔æXzKi>nq=Q<ᕖlpQ~dNn{[-kH⤹}Y0%p]Na®`,<v;S8ń$u mlz -,zNOvyjD5z^ҷc>?'K@*0C}ҏn5t /tgɿO]|ryI9)Z-zӢ 2 Y3F j>}]m@>2b'OߪMȆ\[Q |uѬq~-Ui\6$wz@SwK81T&" *5G 86FS.`{{bi۵ NHvuuK?2N{&w*RΔ-wDWTTۤrBVsloߴIfP4SـU:0.,G)13.( hnW PJOlqպIcX50$ }G ׫RK>kCw7v v Le ( _X5B c^PlGa,/,1 `"::C2w+Hx]z7wQ/"^8 1a*AH-]2u`-P_w2Gx$F0yAux3Fdi@ *8D**0Sd.2\Lb§cAˋMOwc Ȓ1J6!2. n]ch./W-CxZ:t^i7~ʿ;0V+UZo@ٞ:5|kԒsEbN*9W 2p T?历DB+]R+sG_vvq\_6>b (Wu=1\/ּ DN Aԁ1k Fђa>lp)8DŽv׶(O<*9"BoZZN -Sq=lwrGlKPR6 H]~\&oR 8olr5:s}S7&3S#f,*BSRF1iz ϳ'nYAB{}Cp/OASejgӒ}CSB^>&ulzZ݂Ӊ_ 弐spٕUGZ6YNPt)G/Apݠ8&b`=K?G*״{ ]@H􂞙X4 [Ǚd OK[qc˪mfKr2]q4 ӌK+ m,;:ЫPBC2E2!ͣD~f,-!vc2EOsq22vuxjrBke9ܓzTN$Gvu;%\Xջc9̰y{O&W{M2eeYA< <8C.ȷůós+iFK{=xB4'_Iнa:<*U'vL=3YnnUae_Ľ&LX!.kx oL;Iʹ?|~^~?D&Q|X \ dQI,5pDUsM4H^7"?ltյ؂Gd8ۤ{DݑY 'hsdZ47I+l#!0"h*덅Lc'#WO /rM5B`taC0nȨo o z'r/Bm)JBkdԓZ=w*JN={ Sfik}~lch-3:2淄ver`e冟j72uB7b~ ̍(% p/RGc.aadիOVBgH<}v{t<+A簰IyXS+GeKZ J,2./$Vc) 6=zpÌot'_:%s>?1{}#=ict=ʰ3wH}=U3;i1+hgԨ*r۰Em6jڳ [)=%Zh on+dߔ;.-X8Bo&{Hg2ȳR%zܐ6RMt̴N0%GX¥o0鈓{J%~Ogo<Vbkʭ}fK+WDOT<'&cÖ́u'-YIxd ܂`hLͣӹ˺ Iq8&`in"z=+2͠cHnץ#5¼=pcy;yd%@+:*5Yһ(.3,;2*/vy8ʑ bV=C0m%ܞkհ̓=E? V:j"&ǢO`532wtpe.vˈ ?/C|bؤBf/+a6cc{X9,O-|&'W'4 +&Ir{bRV>2"IULWe =)vv.?.U4m\m8SFaNu9Q8G M#Z+Dp-oqayIsh4 [db,s/Y^[eKE䍊BQƩ€ce=Wbf[oQ& q8<%^y+Nk{qGINBӺB>/(VpI{b1д( ffP@P39&LDb}7Ԟ'~,Hꃉp1'6B2iᙗĺJ[c&|džDyXkݤ:qs-DwµdjMnm "fKin=]>"kkb5˫30^(*a|;=GF:i-KG"Y'FܙGgN˖pΦ9//J`0uUY uKY٧Mi^mZҜ)T Y2oro;X]{I9m&oRB)gSNxsfd5]dR}S oGŷ~. ZQbm}q\Z;`\Ft KUd$~6e [9s -OQ9e39/X4';PwLꅅ/H 3EGE>& ҅4k䅝ܡd8 X{\_A KےzGxA[":CUfC+$TjS0ܷHfOHm ʡe^Z'NHzQXcK# y^R$|/'%\Fy2^wd0#B{, ^}8>OsfZSg"R#< U2|>dz.I Ϧ]&&vC$V>(m%OI;7yJ*ւ!ϣZ}!ksBv,JyIj[%Mu%שyvl{NXԃ,W(,ǥf*TTB3<@`b$^5O2f%1ϗZ;V}xތM%WmS1 > /lP Vq^XK+[VqCݮQ$x5)lu?ZVp/^ݫ,<βћ>+]wHU3,5t7q=9&K>8l5~%8z^냿'}IT:zԦjn x#z%ڣ-}roRjotnH A3 ~= 703=ٹsǾ-ͧ IslR'BYnd|8aJ͇.W48iwpc@;TId8p6갊7UEFEDl}G,.Ssq3ZDo#q]3=IroPn?aǏ(IaFPO{N,ޜz۵kb |#%־,?/a?g.nq2:Cx/x{>k|}`ro`tzzS0anwE3OEpe:_߈{-#[nk7^\X=C8?^sȃ?k}Kˤ_}uEZ,vҴ%PZm:5LDcw*aOSۨ.7ErZ1W9y>yǙQB7 c.QΣ,4y$/DgrrSAq#rtΟ~ޟ^o^ ^np5;8<8(WRЇ>t;;h 31K{~.ݴ=]1Gj9AxbKMhw_7c?{!$_.a(S[kᶫt?r!|Pۚb܃$wM>kڸZ0CtXܫ7uO} {\ѐOXUQUI}OZxX)6;S@#}b Ɍ6a~v&֚up͂\Jo zuMwn1^5"<K(J0$ƿ'L;3 B=I[<'V98joXr/S:s1Hl{`_p6H(`pb% ?#i<>_uUCC%/UgZ_ZnI_eIMK[KZ3,i>V-_w,DDZs]jc173vbV_UP3.&ߗO*һo'%ݮL"8L;DI zElS6fdYy^,K Y7s}:[۹sxӾ2Uٕ2l\ZSRKf;\m-c{H?/nTXN5-]F'އQ^^'Բ-q\H,Ɩ$ƕ(.;"8wD %Fb&9DD&<,o sUZRJEo?3GiZͬh(dD"oȋ-]Vѻ~Ah;NYHjE`(hZxRnT翕UIVwt[V[m})dÓ#{Gᑱ6[RRwT>TL]t3nZ: [ R'C< - X0}KK.\oZj}쓿n [x[*,×\p dmR/|mRkXKALxbMB~TlΆ Ja|Gc4-9?>%oW[xe/ޒt@wIHxҧa+ts@Ry/£>HE7pbzT*x4_.Uo^j})7aW$\^M/}B W[.EׁUEMM5BW_#EԎl(qxe?Nv=y/]׺t&J'12ajKaz \#\큚z5܇g:-\:5s<C+sV]|6dW,&.eU 1ӓOX8Q+5 lԼeBЕ?Q.IB:`7ѣט7=@?#bVΔ̡:R jܣhaEQwFV6H+u#6gRung 膫豦bc|B5i;WO\%o,PSR%x7y˦66TAuzvh9pXAW$fL>:QnWϑYN\I?y`5TZ n!L\0V|ѝF)lpm-pM^of=mի;eonӣ0M +yv9tKtf'nh`#^ }?iV gV\yc NQ|v10;wڽIfk7ő&M>M@09_!lr< ?Im|3ieH̩L|tALsq 5N3[wXXʠa,r?IgTPxC(P@}Sy71[.&Ԏ`fy|U25zag? T|4"!s8Ы[#pjWibrzLuG3R+ =2)0(!%s'~*CP_`Q{brgszEZ#8ʈ@̋ Rֈ "w>ppP W(RᘝPg4{[eJ6''&þ1_oHkBDR> U;J#u s)|XvX^1VсpaŪYuB/lSz*^"]O],v?.%*LQn@ثM*ɣFYDc#B;'mTےkorPnm!4jR"s3>qh%8U o6ukWMq6e,4R\M/{p(Lfӗ""w]E?i2_ޫ"=P\].g.#ˍf!ᵫ*ܠ<9VؘibA _5g~;_\SŏMGGfo7UPs[ =0U D^᏾-NG+Ĺ7XCEGdO'7})]Qi"^͞f>}X[=-JK%~k|I S)NW6I/A-tnWA:>ؓL':xG =F"/M ۍ/Z:}q< yR5WQ&}h0BB\ uW's^` Xt6 Y zWzF8܏D )Z?:>u۸?["8C-Dp*P,Fݫ5c |Jn(%r%T[gO-'pܹE(_KU K;}'z3[Bˠb෯M=KB?7^+ѓ)#ũKrJl.8pz@l0cGF²MMGGg/]*H _m}%_)eSR~Nu%Ę1IEE8-Eg)>\|3OX2TMC崯뛙h&HUq 3b}ScK>%pkb(H,}yT4K 2R;uE͛! %iI!44PV_#GQo%A#Ϯ4ƛ:'ĴEv^=6 [wg>ߓk*rx5 -R,SR((hKJ؆ zlzsD#pP?MR$*Og!Rf=`jriR^ސ:;??C ?|l3ԺT+L?X$"<5q3+ˋȼr>L<>P<-/:t!HA,H+W³ GĨpH %Co`׾izF-Xo. SC>ϕ>!ԉMD._.\L^y3aU~"b0UpbiJGĪ<:~4j3f+ByQ ۖ%}&+ۤzqNKMu$wxOP"eO4Jjz)J VFFJ)(\T> Nxbs^UjE&ƅCU Dیp'5ѓh-@Nq.s z{As\M+Igk!FLJƻMCu3:B\(ӯ?NM];K*.DU_/+9x 3=ƴFap 鏐I}KG>Pu(+1~5ӲH,.5gFJYw0_'xњF|uR̙E60G MM1)XY]#Ty0T6J%31o+L QS0 WiFAX ::*4gq)zMe&ICLF.DE8yPr"OU ˛ +\2x#al"sCTZ&ЋiR`z.by·ifġĜZCq(bJM@v+Fz~u[wk ^a[B34?+Uoԋ0~ 3ާf7K7j?OxXktTU^T%*NqSPIBx$! B(nL5IUqFd5<ȞiTգ#`IWiT@֥2M {cdU)*5k kU}{߾K}[W>cccB" nѵl@jNomHfʰ7 u.QLE:V]#$λ+=q|PnDogKZ/#Nb+G0]Xm&<:Ø qX~oO_yh7NOchϢ ;ڻ xSy.]崭3Dy#4mDQR˿MIޏ;[xoV{_Uw;ΤaCt؜n+&XFZM2.ikzτ ydlHCv#S [V-IT"y oZi#dUU͢y $Km_y&uA1S2?  q??wƅ/=;~뿵QdPW$f!0|CSD8Nȡk2YZ"lw}/_Dʷ*_W,\u:ԓ$:7l|8/_ssOM_Un +/NUD4L l;"0ъ*reR^Iȓd=p" zisN^_7/B v^WMwY$gqKz܂E[;aciu>"x*Q0U VQ'W Q 6W eFiwU_[ fmQ7SŃ0{J=yr@VC~B1lY^ ,dYgDSԺ=Njq0W1Ws9qx~=6׮aW"xG)f'1~z,]H ɼr3g4;4եiӕaoFaa:tu3TbԳ,z2MomY_ÿz"wE*5V0{-yj2?؁(r!/N&.Alurm@lTuk@ͻ>KeZV$2{{d~0ϽeZPe@gƇt/rBk+;B؁;J|DmVĮVA!.oÝϤT&IVZ"o4|LԪEjȌ}*8ď(du:(7EJ?jU 6gXfYk^Wb2! i1M {=VnLeAi墙Ʀ3g76 55d-xR^; mKT㒃f+n^y'o2t Mԥd~J1[ YbXq?Qk>t`KeGc*kI\nozĵ3qd/ Bfye,f%"U1Gi|Sr!`'`y~ ,d~Qﭒ}/sU9>}pZX<"xx 1>JyED%LM,ZAR~mLZE~R!;7s_5vwA ?tY.=2 a0-Bw D'+d&) s(Dc7D)Ⱥj0퀜?DۅÑS#KbgyA>kݐҗw?ӗXxrp^* e~<,F!{liw݅l n_^\*)@U*:uo|#  QFeX ~KDHk0+itDj߉PEؗ|'^eŪD,3tVH/5l 6a1MZ~g_=HX.{5q"`d)@"qÆ䌏 anAЛj$x2xQI2p:PJV+cx:&/-fY8YKx LJ2JɆ8n-9ӑ+iex4]Íhe*4q7ܬCTi8!>A"!{}ӱ \c F6PKPl]fz_bޚd9j u*tab0dcܦ}e:Yq{$kM=,m%}k(LMhumDW ldM =gsONt~P;D)yߐؽe v`2{)')DXL:`A 6gIs oHLz7$:_bD3tU2]󬹍QM~*7TwP\5gf&|dcBL.74#AM =Isgxz>д ې4=ǣawEAHe)̷`aÓA?^I̒Zbu Cnd_;ZkV/Ix~brW zy.tߠ*ث7 @6u( !/‹ϓC7ܟK{[();( Ψi^\KTZF&[-e&^l'H&a!śd5|5KWO"̺C[R= ȪMUF#[CKx%'TSxh w>Ȓ||UG&V?1xRkA%lTƶF+&C"Dd$dHv&xS={xODpvl"=~{;x~JvuQraC=DFLw=#cI DI-jDԈ%ʦ0QtU<8?߯8C>͠3pRUa.ʂw{J{ p`ȐuH^stv 2{NhU6c B]şjf-"U=һKS:?푇"܀Kax|ִ$5ʑJEe\&}=O'Vu$sf8UU]\ȖV&>FM8&|Ʉ@+DY"&G#?pK'2ΰao-Ԫ߷lԗ5O}։ןzMh`Y%X1|М?։m`=Nt=Ln}g֛AydIʤbZjiȘ'N-!/>%r[K$)Q)x340031QMNMIe=rJ~[ūNn(fQhHNv]o\ 22KR^hڽ_wAYD\3*FLEyiz Z#xq -9Uʒn?{K [%R P,3C9Y OfA(KI%-.[N(S|RɖHjJs rx%cl>rߒӁ(&o6`S\ri@E榦 EpHfi'6s߇Emoh:raRNfP$Yӧs[p V&aedΏm^^ȭ΀PSR 4b%o=&>y-R$ڼ'񛆤jX,ar{ޞZ>.:iU4 H..HMfpuA*Qs٠V3 glhi܀)K-*fk;-rCߚg.y:xd'x}QAkAQPP)H i* &1fvؙ=S=ċo^wS{ߛ2P7FլWkPkn668G9dD`xxw22 CX~Be>չLrѯ{[fQqX#PPNH0\&(rJU}n_ 3Xv K4#sG/6%9ᳫ/{kNjGScn4[R+0-28tRiTڶ,6ÉJYl!Ptpӹ960b;.pAsNɎIģ~gi muoYTKČ3?vBE9BRl^Fɿ_~: 8{FANȘ<XACi%2¤r0[/S)x}mw6WDrd[כ4XJ~Jv}Z,g/$%;% `8\q|Fpt_8H88- HOq0M(|Na(v}=#0;Ia0p"Au RذpT~4QJfm-N;Su, B?Iȡ;' (*#Q:dC7"!=?K(4[򐬑y뻞?'$t3ƞ"4.Ikcqtl-2<#4%o kA8|lb]Ǎcc׎.Y:/ m',NꞍ}l OQ} g'6[%nWIZ)ҏ{>A("7!r% d6nJ}<"^\$اt䦄W!əd7&ilc>q tLf;Ax3bZDCU| 0lUp 8 p잓OqTW҉=|( _,f""[w2c$"U."gύ LwuCVZNȃs,>Ns@YzO0ߌi q {̫x]j-ҿbo$;ԪL-rOY :iUV۠ԣ8M| "W.k).ǝз>oo-G"_hC)%*;yw4#R|J7H—)ϯsoN?nn791$-k!q7&O~sBh&dkWdk/EWd(kB흻ק7Hɿ)BIX]?n?qۣdQ2uer/\a6{'t|Ne&L4Zl~mJ6o[fJ*]RofJ*]RoOɱw$5v N;<{$崽U=}q?)Lӝ'L'ŖJ$'|фݍ8'4.#;g>p6 g cN8.w(kv$%m:»9\.A*#mTH/CI2#7&k{N:(JR'|u0b?JKڜ$#ȏ}XL8`vG w?LuC|~#l~lԟLG f֦V^a ~8 1|Ebqi?Wl WK}%2~KS1sHzVx$Zhr?۝q$_I'awo {}ې;ksb|qQmX/}0T ]F3ee<s^آ2YŽf4Vc64U޹q*F('L<Rid,?_7ONmuތkoWI9I^K?qA!Y caXE&hr3Ⱨc/gЏAGjO<ّb=!00 #Q6Ѵ?鏳}C<6Xef33V[=P I:e&xjF0Ñυj5`OLZ#Y wMGA#< H 0<6z|Vc1Rg ڻ5<2P *J=0y~kL4!<4u`}آH<~NJ8ceJ^ YU]} sl1KYL(NkyTͮm8G Y7Tu  Z=Ik8MRgCO)~6/T:@O}i'e'&N#h"u9u$3@ 5(OA#M6FZ>WX%Zv2&c'IXP-=ʠb*hZ]T\4 I|Pz{:m{9>=y7c̠ ^1rC.I{:~yw>L/ LæSѾ_u Нhx),y@a\iZu ;S;>쟀’)Lkf-lg՞bl^zEv#~fjWބ:r!(Ka53C080K"2(zPdy6r(䧒zA4R nGT:B1G"KI]ͺ29иNƖi1hºha>~5M 3L A X]dw~6 `cdZg*u^vN kt LR$}$igW#&hii,D =s9-ty鹙0 Nٰm } T;A瓷e{tqc2wۯoUd uKA>f2/cVX0nx~MbێL,nU$+@grl(М;uAz[M[;U͒/ZKGw_T,NwzGҀfܓvwO~F&DK7 +\zg,n0#f, UW\盧Fi{s;ytT."U=r6 |]rh61Um,j4\1} Yo#Y `p-WZWz$:sD4 Libhhr\8?N;_eef0#k]+ܜۺm.ϳ]ftWfVđ|ػsU|ų*ѿN__dl$HM?i;.W\y1UCSA#ًpUsBaԱw;yߏkVV-{!2\`V܎= SVg*G ֶ&73*KNqmrtp׾n SqQ_RJ?C/.l_AVr{M|cYi 3Z8x;Vl!%iw>TDHę^/uC?檀2fa*=?"ؼ|)}ΰԧU?;u 6O2fcdȰKyЩhdC^6fV,|lR&CXz2o1q C@л+jߟ\6Y'+% <]A|3|܀Ι,PqMmh/fWF)N3Uٌjr-䊬OJbT]񳅅 cbT{+bxSi#2-+$yGBeųjڲ{.d+{dBCw;.bRjU$XKJ - 9,cR|h*tͥD@MYO[~M($8A\buVONrf(+1i^4pC:~|LPO]MlP+miNn8\QVX!Ր9 #a:G.Fsj iZCG+E84gLỎ/|z=[;9=BvI@ju91hos*fP^<,8o4 xzF>-_e\z;I$vlJ 䦢'zGa\mSX:+}>k~XenH>l@|/ֳg0ܞIO`ᅍ~8y>5p݆{pOz `@=yI.6"Y'`Y{ԔZ#U,Quc[e5ט&*- ~ᷡgIud7݂T=qMĤ砈BQ"c15F0KM%XgMʐϾgiC IՉ>LZOukh;R\E3-ld51'ʓH3S4!Cc1sS<i̠3ʙj3>Zq4k9ɂW*ʄ ;& ,=1onqu$'L9_5YC , `MƳ0a[S~A㕫W .M$[j`=<5 *8ɍi%Q^&M,0CY~:se(=nJU\Vl-z݂k+R8 L _zi5|B /Eۿ4œlˋZ\wv"V,}ySr  W~ϊ6W $w΍…?XnCx-J׃X6׌l߆$rXDTl" SJra+m#a(!t\JJ*)V,]13|ͬljfTy]9(l}]uWSSue TEf1Ab\Y,3MJâ/tme#`lsf2(Rgd-Fkl2CɼyS/XT ?Z;5\l*t;]@Z:Ϧ*G`+z)7y^FW S7҅Fbm3_}7ݙsGiq dߖTr sVAC]XM3E:dmz)8l2h;E=|?Q]D0<:"XA+B͑iܤ"?<g"(48qW1F dEg] -wWr 𹷘9YfVap^;1^A]eclw).`*JU% ~+PKw~] I;dm@6Kvv6[2Kdic9t%-7oC1|AoqQp} y yo|F_jFz,f㛎U;(F4j4K1}S\ Ej-2F!eDn#֥fV 6u)m!fqaKnآ҄*$TTajq j7>X@2+`q+y6nM'~u^R7>C~~^)yI/:a.;`DQ4V^IQ"{M!,i9_|-`n9D[b1X( emEU<7y BL`9-mQ~A? '֪ }62 {$(8談() Al])Y0` X$KiY9'ISt\蜿z]05/'Y <~Del Ȋ(_˖ŏ|{W&6=W|bf 0 ~]7 g_ *vjIEw :Wlw,ߑtkOwLYS @2&.@eE[⓯ W6]9Gn,wC*w# vKEqٯm>N8vh*\Ieua?;PSe(\TG7jL6"Pr 9fu*4Uff=˒G<츆B<+njShF-e^*-\j򒙡 Ϛxgd1JUs&^b `!N[ %`gOD:ԉBEK9OW0 ߇g;eWհjK7_݅'<=*Ưiyjl`{K?Y~S6x{ (Ќj/%F@ ۲GkA\\ BвT~yNF^0=J+1{ABzDYEJP$~ޫ8xc4Y$N+;m$aݫ3Sv,Y! C$9iHN{B0rm`xgWN`ƠB&3#QGyy ;EFp2|;@h} ƿR=`BbvFDqIl&D #\RrSԙ`90gyU{NS{atyn/pt\Cx$H-*BǔK({^͔/:AlGP NӀ`2GfOr)sTqdt 4CJj\M* 2*O?S,(%.Iޯx=ejbDxdM)yڬ9|5[\E|D"Tq96GiM[jn!V;{UViBeySEhퟞEDf5&LyW,H_^:3e>li@ ,c߿t'oWy8.j~ԉ[2[[&٭7[%n( vH:=`{A`j0_{x{D8M#ؿNFny#vK$X3x:`{6ey:=L)ֳgʆ&wz?tgc;0iҴԍ'O+bZ7DHۻ+U n[ caȌ?}Wս4Ϳr($Qz늑ZHaYD ZJP,54@FbP?g 6'%{RsC$AfPCfyUw8aZpu#f7mMB֑|<3o2R@!OcMj%H'IFȵ1k VAMU5WghpW2q9A|f,a¼G_qG%I>c_qʞKR(_EZ=)SL67t}YYY48% O^67ɥzBSgrDU*{HuL_ŕ&GV3l>$e~;3B#?k@U>? C#*x㱐G`mĹA!tB͊ _NiHNX>9-{ǒ&[)/߹/yھ<,iVY5.1<O]3Ft&wn3-/ʾX*}Gck~e5Xςd/`a5U[Yo=ڬdjq<.xe%^{F2-\FiF`J)whxaRYFT{ɂb laIY2==*怾d=>eqg>*N}Qn@+(pp ʢ:RL,t J(oh<ĺn,wG[/_б5TذzWp/xz(sV=;JSuÒxX^,xcC%F)W,K.X[f/C]drK.ye-.XQXn, c1-X$2+l(X<=2RJ`{uN1_Vq&Qo51og`D;WNo@|yT^QEwlױ:}^xO=2dЂ^q)ZaS`(l+3a~rvR8R_+[ UxP,ئ?ulSYئMc6ئ;#m5"/Q m_ŎF``ǣw+t$cX߽b)I,tn!}3"R&fW^YhBoyS[t՞Kmjf)%~LoN gOwOv睧#7-X1噏Y왙q@1+=|K]}O%pig:4="w2iMe=H=vKӽV,dɷD۠VEKpMiNl| :<|@V y :4XXkZld7ecij nalgQ4h1wPLW7,;"x4uvۯΑ`{4IGRpxK&Y_SBn~rA%N<54qUؿdž xWyl-oAAvಈQ24#96ހc M.nL\k8roO!ewH`W^?Ѕ=<-8| lP[뒁x/5I4Gh\CS R\6qQxoņS3WA:r<~DN.1سn'TaGS8l ,EU0!qВ:yz~>Bh5g7e,@Xv y5O?:)^'Q( b@`0B*'Q0Eȹ"\TPROR@Y=x7JaW^g[[-d3TnWl֋{aY bмq06\gfRũi[JR+9PM)'ĪtKJ?װzxX_l[Im!itK{Nn'i\bHlc75}}{MHJ$ۊ|x+ PVMlL{`T$$x<[ZǑCu*N$NI?$}W!b%bxF;,l_+]Ϯav13jm5}eJ)/0 dVtxOpN7}!#İEE\@EaL .mphC_si>T2.^Pp:Nfɀ&\``,C8I`3O\'u( "tD[1#VL͍q'l:IL܅4e).ÇS8~[nXt$M1BNax[+L '}v9"0`5Y [ 9>|d6TI%ە-ƫJ,yk*Jf.iM˲gLn ">D=L( (3u<f9NO''3>y( y ZL=T$̃b*#JZBjwL;~><#yY"J%+)8Ƅ9209$2 q|]0O?xDZKp!4I" X Z!t| mnBc8.k׫wGLQ޽b-+ ^\p弣(6>x~VŻOPJοc:rEEt v@S]-u[%MmR Yl[N W"ƠL5j}]oh#p"- 23çR]n\nք:|+7a%xp'ل:r \j:uY^s W=h.]Ѵr?Ѿז޵-3ꥋTځml6(PgGNjPG2kFhQ}V-Iq .G'CatDчIW(Ez}۳όb*ļSG{ѰJ̨l@}Z7wzڈZ-u%=YGEFdH| 2g2[ZNDx|z`/[a'S̑CT8^K{нN$UR뚚߅>R?tWޅ3PݵÏ^/5|=~KI;#\~T@$V/+oUˋY}ÁNIlÑqVJ +rZxԗ?WaiݘSˬ!Ȱ\ gHaUFa Wmvg"d|߬ 2i 8صY=1X<3=+3Ɲ>S.#oRghiֱut=O/E/^!x} t[uy2%IJg@$(Q D)%2 $$%ʋ4iZҜvGiObIj7MNjozUKۺXvӮ;3y$(ћ--ϝ;wo{JNɳ_XUEOo˻ywyvo'LτH(BC=Ie|lSom(Mr(b.ߡ6(8TW@Nw'crPgRCv[x>BT"B'Szg{B\d6L'e9EAA;"Q8O*k zrt{=^n8jS?8~`jgA4fFƊ3+KO-}gQhb)=#(OO ,Q@ t;սF{"49ceGA-4Ϡ994h3ϐ!7*D_\ԍNdrXP)j)1L.3tF&cBm6B(yDQ)Y<DäVmow#r;Egw ܇3xfpv<. mC>͜OjIhd|bj&W()9t@(4Q4S)9Md xw4wf*85rBҙOmY9IG$qg/ Nwjluqn,NA1MviK%}I;q:G'MM(+CRvlAz/2{rfS B$P0g201 Z8߀aKMQfB5)Pj!% LFVs2SBG:N@soۍ:9^E@ y =:cY,[EVMD#dɠbthh/-ATsE@J>TJ4d*\DF۟I$bhU3I9Ο9=t*QDs^Y]8լLPF  zjY7wYQ@$>o05&|3PPxvQmM5j?vRgCWC\7Q[.U%nFW\ݨg?M.<}uMM=Gh4&B"g{y%K9*W/Sk0H`'Ns5&i4 5Uf13RSIp4"^?.[e.Qf$" -. {\}t$#@QpQ=K.~ęNCYUI\DQFXLknaT8u;l:D<+QYh& E.5bU+7\mFwҜ!LekՈ- n ir_l ^ kjF2;wu<1&gdpS*ůܵ&|T 8:]wiʣ@ F \:_g.ADύ;+8zȊ5# AV EB :CX KǐBF/+[xhՅ;:Vq/@Cs` VT4#4;ኸ/ 3r.4qڬ: RAκs<+ƈ9IrFc;Kh!Ϧk*(6~Gv2;; F̣!*.IޤRgANBˇ$uBƕUN 43*M GN@CgcAAL7Aq1,^9wcZ9 k+ۤ@4d#vsrzgg=F`r7VKM%v"Rڝ{6%Ry 0hpzbf_|e+^| 7KuxonV#ׄ8Z}d),-J IvN:Bt7)e'T"g'ie}RܧYkPKA@ IfuICG|.~u4¸(%0lx?`ۦ!f8?DS3@l+gP7h%l")҄`-i([T 1&7%IDRJstzbhs9jSǽ' n#wyS JQ.SM;4dSM݈[S*`j0~h"G jriDR8MgAl^Fv2@ GNdtDy-nGKg9XCh;NQ99oуLiAJ)Nz1n귢&fE'A<>r%$MG*ۈ{jnδ:/cEYW䯓7?X uG){ 9 f _?ӄkjM?SCcԱ[etݨ(M&HLc"JX}myAf>!P>GidM m2nP߰.Dyhc³[:" B,gKg2HB2ΰ7(D׎2X8 yU<u8j !r9fi"ざs)~1 GGTbV/&:/(|]@ozM7\EqY$ێK^D9w~T1vEmȩX;n԰!j{\-vB0k AvRxj.745b]I0lTËbMaEvGKd?_{Z\KZM~#bUy:I: ѣ J BR!/{b/D,z$WbŊdcsKf SIe Rp|cv{@{ht"n./~PC1=UkŎ u,D$V\Q=p=^sb-{d/uZjGz x ?¯c䋣 iK >07lR4V 鑗d H×/&e7l_ 8 &uW?Gf+",6'$k/PJl[5+Es%Rlv 9Ѫxh-;=Q;sEO"Dhn6)t͟$baP(h< ]$fD m)H<qR9{1q:~bv|@UL87SrN,b_ѱ\B^ei+ESMe~J>]y`|MI墿]j}ξ׮W_m?>FsGׯWީVMO-'qۏ7ӡo?Zg>_]?+'KM.sCHTYO+&>k+%w8 j6( [; $-8{颫T?2^]cˏ9?,]Sq8h^lA>ATog6n&hή6O=Y[U.5{m;^7*gFb|xv>V{G;.5j+ OVZ76ӈ¾xb-~mMf!KAHt4  Ջ"J%ю/\Q⿹7]Ol_i|F'GŜ`_~kkW7?*YFe"<-hx%|謾tR㈈]NQ<"޶O6(A ƈje]~[Tv%.1]Kofe8[i|:HT[er$Oj[ӲDy 48zT$։Lb5,AZ` ޣA4gKY[ß!ڻ仆- [o ~ŵ$b/\޴q,>M`&'gW%Φa*3i~wm||q c N%<#-wm52F]ԨcM2\g;2ē_l<% (_ڸ}8sR;6 ~#p3+L;髶'O>yƍlIQXN%e[jR{a نLdOF'=Jc6Ou]8u Nw}y+)FHi3L15>cvEVM @fF܎)XZN:{Qro_T[vxL!ΑY -$n8/|WEl6 )* ǽ'Lܨ&nUP7qE^%ai}FO,#ݳ|%r.ؒvdN@܀iKW6΂;RGR )S(%rBEOzP;f9=)+CoڌN8B%/~9JS/CɄLi 7%$D\ډ?S`.۷G^"Xgd6Uf4{z jGڰ_~|4ez('t׈V_fcD`E},]PMf5nsQ)yͥOuVY ro/Q-dph.kQr%a ;JǴԫI7CXШfFoy`0"X6pWuQM.Lį V-)m9 F2r4V-?rʦ%1zlsL[\1tIX`exfCtU[&KT~Cm PX:$StUֻ{ȚT5w/}BG橑k~& "j)o[ކR7+p7wIS 8qx I2OD9/O[+G~X۟\} W6tyo `}St'ޱcn6 ya[xco'@āi}ll 5o呱ł fAv8X~={>[!Tl]$^V+ 䙫[X?JyxBd\ZaA ߪ/}ƥU{jjj>TN& X6r$#AȰxpv JBbE`7yG0](ejRT td/: _6gq3CTF O]:D{=چĒç!I$ҏmF|`4ϼfcSQvZT%v oJ, ->wcy}T ~pE9ż]/ a~߇Vlê{UAX%Zs|Cw<) Qpc௶)!*06>?~džv 8hwB+}&z$iKJ˿Q“hЖ$JI~VI {T%ˍcseWVIp}B}Aۼ-mҶ; {T4iZ)OأQBxDSs6 #T=P{mTww10sRS6& xf5ˤ|%\TKI ka*ܟI͹ecIǝR7QrCy qPU]j1WU{.m0M.mݽD}ok{Rkd!> O{vNdyg,T+@EEQ.UHXT!# gT'hƁ+$u/*$xԜRI/NWJ>px0BO:fR OL [fU:ɞ;N*ӒI|+]'mIyj:Tzikj^ L5&4oWBhNM~gR<WZg3ށV[Q+[6Ȱa F'gtWDjM&v- <Q+I) >U(ath)E,ThNR2*ށ8(c2*gF>#MhT*8} U{0ΧSEPp{nZw[B)Bb8oZuL|x; xSE_J[z}WJO6MڴyMKK[Bb)-rIIjEW<Y}\uWqW~zUBE ) ܨtT,С(*^ߕDQv?٠ InMt!*s[bAEFKwp-V.RC&*?扲&msa]̘Ӏ8u[J8ԕez}(n* F$ѡ$aZ)?u1PnI(=W3G)1>MVKř==zT)3T֜SK :60bg愢+w)4lar}q4eX_:Ovk r*Ѵt,r伇QCh]^RHеGIp4u^?%65aT'jyMe|-*(֋TET'еRSҤDŬ6d#VLfO>=7e)t|дzn~)* XTf uSժBJ`Ƀw78J\7OK$}TR`4 -WKIj(jH_).F#9t "dQ;DJ&"˓ĄO%LafY/imۼ;H$̒,Q3hNCNe&s5H-1tt3ƹ- ƿc9zvo]qI}UsQ3s060ML7haKSvӅ/k^,VֽP6$zXYF։lٿL=,]!)=^w-/ Y!u5MiLjM(6c/۬GS *ca J ɍ8X .rvdgL:H|9PE-R2/rӊ: شE8P-s (nߊ[3V)y\(s6GLEZe>p0 y9n^E |["f[]-5yX ,D/8;mImuiDȽ;,lq1"'o^crc1J,5H?6}20I6 _ɖ,:W-\w2-M ;r}{4 R{z4 cP\ 3=vU\Us9_yʏmFęB'kzZ 9O3AY6<&2G=p@q ږ4Լ־BSS ;7􃸝܍ԝz %E^!! Y y('wOwOu74{nh%tQ$x[戮ĠyJd2a*Hr`d+L`y-s2xw/r6DC׸#=aԯE'X"kZ.xŨxgsgA:8+᷌9@L\S~{TGL*lJ+ #/wҀ"yFVC.r"_si rVKh&@8I8$4"`h8z(xgy7F_AsIĀq`AfSZ =zi[21ۜ?q[;~EyX ?&H 0~~$TI"U߲ocGR='E2(s4=3 ^;+uʱLI'B.2pmY`~%91d׎a. +7k̔H#9D H-UQ$z8Z;q'Ъ~b>=p ?N+oO sG'Ugg`("qth;bx >*F HE:߁3$ {ːwv~17efk 3'N'fOq=L~1ENM*Ow ;JA|n\8e6< $}@*ͧ5A|d!~ ^6, |{ξQD> ʇ BbKW7?P|HRX1H/i^Ҳ#R96Z/Kυ1P^ra#gՉbuQb,^ NNJX[ snO%C?l>'|xD9_X^3L.}>#Ф/gV4(vɀ~Y ^"cԅkh`yEWA.hQI٣h;:+UN{_M&nyuF (rm `j1YJU>+Q@!(S9]Ws VfNw0Sˍ1-pd=M]&G/34L0b YqBP/m>5NL/yHH_/x=6o8`\w%w@a<,l86bqBå \4"352 $$]B[Y}?x͋Nɩð-o*NAYGdhi56 utvu࣏go\jwWE࿟x9Cg&lO 1] דp9UTrĂ\5}^ T]{t28֟9","E0BT<ȯB N[fa%q98U< k|ǟD8qnN42 3<KxUoGW +G& qRbNZh;ٝ/fǎF=s俀JzXn=jU]%87~}ie۽|F#Ia^YPa8l~KnY ]DA/A61)d63<ꉝlx⓷qG~"juP(+wdK88PdHCkF`6Rsc`LGQ_IL:s-+o~p s ZׁSׯQ)̣ c ) f${N>Hkey,j.Ev m L .o[T" ^5%,s`0顾7ۡ2YJDKO^ MaWd١r(X4[t( p {Ù]]QxJ KT#(-I(,$~Ө Ѓ37տ63&Heb#p;U=?J…mHtu=ЫkK `Uo (wYQ9Z XR0owYvf]\wdLuGkgK\q+,٬/XlLWvT:DpĄq!\~L$TGHKF&|fk5{R!/o 'v䐚Yo5UmV2lPF0؁ua>$,69%VlEˈf4z!袨kBc Mo$tb<vz ~KD|IoW%ӱP(='₉VEL,|Ēc͝<;b#v*\bHvW':X5^DtZ'-FW9ւcX}eZ^wfT74 XszCԨArն4'==q'xI=ىFMxVmoF\9r29RUI*J#D6E^J&چ`cEF23=˵cf<{e@(YBY *"YK )0NSJXH`F-*hNȽ!hQf .E9VArbsCtcx0(J{.\KT~q޾ {ІHWz9}ȨsJM@ sg6!T*EİP0% ;\71Q,8 .Y*)vY*2t8 ))2Y%qUD5-Rn"2tW)U5}ScG3An&]-f<1{gMbZLA! v4cQcU "L_n/^tZhY)YdYh(ft!;ifַSɃpꙷ@ΉdH8ìīy}fr1i 8N? mge+ H!\m»ssQѬk@Gv>*^ڬ7x8nXIAl-$[AUcfl,̺_d<ЭkB+!PDqBa.W> 0?1sl0Ld^WJ0`JԄیL|QVʱ*@եL%`NOv(<^v^xݙcru`ҎJgЭ'@b0h ΅Q5Jo(bsysM.Cn?1t[TA aM(:$|tϛ8;Yaϡ*:~a:|Oy`FdKb/ߔ?66]]zXBCb(n;{.a ^~=c3$ӧ}{>~yh&A(جMl:YCy2(/ Ml)x&~I|C:#4(x$I|Bg@< 9Qb̂T2} `E& 9ͫ1+VVƛ{2ŃuǧU*Tsq)Mbj2Jr3&oaUg*(ǦZ\RT\#>=jJf~rIH̜<#bhqbzܱ@ srRbMI/\y9J0EJ: >AAA 5-9'[M잓N⒙!6f#83R4|&@`a49?%5>%M%!VJz+@ w[5vIiiEG&ԁE@1:lsOkH|H(ҊRS5D'r)͐>Kϭ99[Lomӂ xun0]o.ED#Tiܐvmv;wÖa-߷p[?y3[.}/,ϴNFWQlO` ͼKk揚z- `Dqtb́)˽sؾQ+L"7aAhtF툦O;~񛭟`#=rwpZި7;;PEI1͙!d>h2~p2ᧃѫVt$VG9`ryc'w V.0Û-ϏIқ#&OHjOubn{~ ?{yo.uA&|:i)+E&!K0~4n1qLgߗ^xo-SZg{UZfqH \#imdx3zskr nY`|rȵb%`д% L6=Ԇ!q⪻b7lQ^\7>H:glI:TƒNo3c쑙;?JoB! s]1ʲgƽCvѡu;fض/ 8dvxJIw/.gk Ar( AY>9/.ګIE s!t !65,mD؎p8eOC2 $qsTĎ-;@h]epF -.0#>)`mɨ! =p(s(QMb"0S8,"yOuH5v %]'J]YOnvt"7`Ā ;aDBzϽ?8FgyO1g0d(x\<>ago6l~M? R@p֩F<ÜmuTH:%OMztM'ǿKǬKPɩcٜelE7%[flŏ Lh,' kY*SXvE(x )HVdk쓽`h|T20&) E8](T3Ypv:YQ|:8F {Grx#\c~8֤,|gBzGR^3jyW>,{3{{=AWU&)0^y]ZKҮk~7/\m`vp'H =%RSJsJ\$L`$[Sbm,O`y5e D0M,F8 &wbgZf^gA%d+̵dv \*_|;o'c4S${JŘV]}dS78@v 7S8-\уjT߈E&u*%9!ik׋ B3ͬkL*0ҙ/,`c{ڹ*N3uCBFe76DnBd1Nc΂ Fg#S},*tw)PEF/]hX 4}󤞈*)ءp28w{`cB"g*E' q]*v%2A :`bK5(6ly"MYoADKM&9 nQ| i}dǢ~#f}kSh\2/)Z %Pi<ԬN=0Hq{ebm =+Xt0Cۿ,Zz,cyk/`[ȕn%KCYd$dOX&G#!A x1cC>` zPs4e`p5B/G1+fb?ubάHq*H&y!,֪MOP޲c= C?cw2蝜aK_z &Z w@Vz!-oc)q_51ppٷȈREJ$MűȯJOjW$ݲV%‹%y"0:?ZG_> PYoV;:GdS7OsVa ;*%I+'~O<>>7 sW?~ ʇu”GZ/Y¶œI >*vxkrg,pĶnA(5ӝSUeJL] IoSWS;Wy\1 VDN6ey3 e*)lOUUVY 7b 0liX.xLW5 {CEI|ҼYxnSX&fnצ xBU5_VR\12/.O(,I(}(z? bqEVQUf>r6GUr} zA/P{45uZYk"w҉ kPJٺ1V"C /O˪DmZ3s(,KZR|ѷj\XhCv]r/Ha] ⻤IEx Ί+d4?d+Pjyy"BrkRۧB?ƈ:UI[hS ţ`++:=U)cG샢 O@GYqoOyি-d e.1j Je:zgvZ-IZIm5Yedٓ@Mφ]iZjԂHM޾] T,q\G lm\(d2EPIeDE 8mUTPz6/+8%Q'bgT+yD ?OS^$}_eNy%JuKpau6kC*JaTO)x)7^ƦU(խ*+E8;{$0'Ɏcg**YE) xUUWEV[=. C70f^RO(dc\]aq VD3޲ W1v- !Ue؊Ua=2]bE 0/`ˌ٤^L/AY'U5'@f2iAI ij!6u8<7Ǘ bS4}#+0;"voJ_ȑ[d.u_,hMjLuQYލ FSZ7I9O׉_=$d犠9m.6Mg8L3ghUV[?(B&EX٧cʙFbfI?उ-(XqͪX\~}Lt17bEM ޥnnxzI ˘6bN;.Ix}TKLaARPN#@diQ~K1)^2"PtJ\ؼ/1 q̡HZ/p{PGw8=M g(3dI9zi!T,I'Dp/vv ֳ`I%/Ca6*k0 ;aѾ] BTR /ju0.Л]U)Z}ʇa$^,嵲AU}2* -"xdˇCW4hKb)J<4<@`Ur0@B%;jT˵&:9]l1Gj5xaf|C~|D_V-H.sJՒ;gGEnYРIA#Ύ⻅QsW1//1Չ4C:4:e#x6%Vb&ХBⵣ0hO?w]]Ne c1RϜENCȺ:<"N:aI {n;(IVˮMƸa‘ҁݑnpKթ!f^1%(xJ3|P= dE&{khNQ>RT($5)`C:x쩹c%)e e@(Nl`&%8%K' ONp2ՈɊ>j`!Ƭ “7N>N7O ` ĺ*O 2j]ȺYǑ dVk.rX& Io^Sl(c(1N$|ޅg0.P܂N}0 Vp2E C'jJNJ-5'qEr?䁘}H63x۳q KDM L , ͭͬ 6b-N.ΌOI-OI,I@g@'BY.*YT+MXh]Ctt%'oM@觶^O~'؉Mo:=46EP,< yLr4YEsvo4E "Ϳ6z(Kco'ŌͳU{nO}>]dУxk&jo4b\뛆RTM|m`1 fEsZHJlnC&nVpE*vLNxk ㆥL F&F@`hfebhejn0Sk^I~F Yk,6`d5=D\cd Mx:uByjf\p.5Նf&& Eyiz e>.yϵSS$dxgES򬾷:;!_ ='3 KW 38{T/Aav"lY(7CƏůV PǤE0 Gx{UbC 윓6s}a22px}L&O, 9>k^NN}- -}Zk.u&Nn0yɊbjLV0ao,4Au`Fb%Y'g)O^(.d^ѝ+˯O^zE4dK /N?Q e'?*>gWz'Ͽ6y7\w]n5E -0u_/Djk;\) ԟxC6 2004/02/17 16:34:44xM?DhOEQKJchar *h Lx ?x:uBk=[0q2^vミBfAQr~^Zf^qOf-\?,p|ӜF=B{NfPobe?փQn&-([Ixv"F-oxuƛMcRF4x}kw۶W>oZ9qI,ɉIlWvdimE/wRR.{{I` ngV=^Mpvt U|=Ki4N x~ I^GI+8O*D$WP $"J *Sׂv2OiЬQ<,GY 7 eNZp< Mf*hYЋ(M%Y@?ATV{(!x6$>'pm2Alc~46ugfllPoʨI8 t+*@TFeFaW7+Պ#bOқ`fQJhpWxQT YvMév6ޭdt6.Da4 QkF'ax?>x\h؏?X~Sir/Gǝ_8?[r)Iy߆{NO@ *O_|ӤP )+&_G($Lf?~i/ t;M%!)"CcD [Ntd6H}pѣ5庪*ShIL.(/oSaA8Giz$W2=\D.C釆1J (aNÿX :rUc OXᗛMr<Q/s8 '?~26.R,CGTml4ɇH1:x8mw_C4V!B4b4#y`bҏ^3d}V>lX5YL5 Qz܄)x"]U 0QKo7(B N6J*q;Z0F}sxC$WsQ.aW<1mdLWKv@TrP9*2VCwxvOxxe$ )k/V`&hh+Y~oWTQ]?8osΌ]9W:SH7ҶhTris&y/ 00,"E!z1r4Re3kU,Q2O,0X"K'U(S_z >ȟh^WCh6.A9Fsj dUE^G$C `ʡ1Zc$Gh8 BF_ .ꧻ$Ȩ{d=Ñ!7 L<{uby\aky\7zpx} C )ӸFQJG#Mޠ^IsVbn?MFR{L!g4#{&* ɂW dM&-y 'S2[rN4z<fMmC֮iy8eb\3vnRmodg* yKtwG5_r[@Prv|5,'a:FeT… /KZԖ-sa/mx{/-kh? !J+~KherZNddNϸ\ͶV[B-o+!Aߚ96a TM5,3:h!rb}NjG7H}5;ѼW$|%ǨolO!,-pWQjgT3{$U$'X$"*_#|O>+D֎֨E#M&4:y@l !+2bm_R7Li2"_&H|9%Q\ T8i~^l*8xU)BzY]V轢zht0ǵh [k|7AП4MR8 ӷOY_P7E hRq2 HGcB5/dp8(z\G\uyQ>5WO_Vװo- Ju@ n&nrsi>EC)C3_w%G*=z랼^N{q 4i}\`EE3CZQC!0ʔO2-4(|%QMO4bٺ1E=k."!#.F6Mz{&*Lf&/(Et0,Kj̃Uq8LeS$Z:o`6-0z"uMD|fNO?Eښ,pCvc57H@0߰NzĐFCj^P~3E* p"$!h@~7ڸ`ӾH4Ԟݰ76ěﴙYI[sS, ﰭ0[GEwص7`~:$ i/?Ň!Bcoi21FުQ>- b$#AlaQg{a;QF;8TjV80 ө>;w&k ;7C+S^R\{ĜlßDCdVƢ:+`=6̇ꮤqn"%>j^Xb/4>]GDʐόN&!Q:} A 򘩃U[~DBdZH kXr4kbQR-S*?O"*EMp PդYN/v+}zQsƽMal4#~onǒ9&DeX `Na|QBQ:1 &_Z`im:%mtEwt*^XKjEښ 4hP|OX}46Ar")W=A = +>`HSe@n~͛qv:8[Xe|I1B=:~z6ϖ|j7%YZwi `[{<ڂh"з'OunbM'ʗXõ߰O{'/xV_6=@%ϲ@{V 28ϏN-υ<9=y@R0F!W,󥱗J1Hf3+jÚEjX ;c^f{Xr f>5W/ǒx- !ZZÔ/_!6JOi0-\W~oN`~q6Z rX;UC?bȷ::6nJ)2#5_Ԭ}sWN>̀x!UaAP"S׋\&irgie?QFP0rBIxyv aݏZxU$#$ŝ,b.n#0-yeKe*/翝voO^_Ϻc-7浥4Z M[L[\v6 p~=Id`"vl. oԶmj鐲kYo> U6^-6#f6Уdz)QDcRh!HUMc%!ޠ`ڹoߏ7r遼hEpӂC;H* h>&V8Ͳ. @  Y<~W!CR{0]Ȧ:JU}'k~9x!M7ox MqmGϩfݺr7\_ 1b&:p|ᚿogGT}˵4g,yҸXel!R2,b[A)SnY3#vq $7&_!/7|ˑAWƻNf=6Ȯ]hi?03@g4ٷ^ ұhNK#l3p$:'~X="u|}S%YXXc5Ѭ8%u4Sl74Eێ|~`؆9nuv,=H)"]OfbbqdӉx,+:O}17l[h؁ QFMYhiՓ QM$Ϫ\*r5 R6塀m~5Kӡ爥DX`gu.=o`BC)[剙/|dF#?-6968 "( [:q eD6f%~GpʊU@%g. }]J%~ v ,!^5\Sw+[,bԑD K#B RFYdKegOw8V!\G!TK bLS=T"Vm%+%k{Pd1ijᅨ|pPS/2-yh B a9ن[]_tƗ)Ѡ ',2ķj* AVM0SZ$ԨXD=e(@si=Nl/\]8" ={ ྠȀ\9YNe",cm)ʎDS˘~WJo2&ݓ[ 1yIR*Z"8L;2D0`,$2:ˢ'KWuF  !֏mҴlA +Zvfh:JTm7! fc@80:_>\"*g]SvEy4*ql#Wd$5a}4}\<M8p\er+tT؀=˻(Q-GHh_Dց]ůz(Zب25puOӺCi!  & Fpc|CSA` m iqW\`o%޸  y/O%Dw9#ǘ_Le"̀tװ 6K>n_Dт}"4ߴdi<;E/V$?棳a^uzufMFj {l/7^ @/E/e zd#`7~L3XllM9ޅ IDnHܬ[[h[4vҫ]0DwDc6E&smtD'Ո.*Ihcc=vp+Cc+J:PD68Sw#}^GhKb) 5VQǶ4DPp2=hz@oIk_"'I1?!Xaak_{*~1$Z@>pt|ASzGS0߶1VtگQH51IvshȔ_mUש.R}sk{gS-E2Z/(Ӷrbv[{zA<6STys(wF;hH ,N}D V>0,In E}ʙ{Ya e!St x9>>-߄d4\`/ʓnbZhe/g5Oi<1K=3%bw9w ٽ;>~A-yǑx]m><#<0 ZA0VJlvS8}捛>#3E8 R|; 7iЌ[Wz.MKJϡ^sdr:VmYFØX/vC+)Ҫ8d1m%bw-þ]Ɉ;n1EUKo >7.,86 N^N,Gs98aџrނa%t,XƨF1R -b9'iK BC*ت0FybU'a1ĊJU|0 ]@ <qs5!LVh;UK6Yf*9:YI}d'_(~ bNf'RɧD;T@O'ʓgSNCy\`fSɏ/J]?by"xc=JdfP"G\ǮTw"u HYxB( ;,b B@CdR}M(WV8&*Ir"q%~"! z@`Fm6a҇mƀf(5d$`a%\Bo}BLB%HI]2o fbe>ۀ=jt fذ<'e/g Z$[.T(O95˘{ݜ F[҈0W2-qւAM_d[/e/Ɍ(^>Ag-S;z\cT~3Ө K@?i4fYNV" }1$|䝓Wb$gcK ,϶ULag0OZ [ X,85?.k3+\ELeݝBg .$T!̿[UT߱tZ!x\MSΝDu;Ѧix{ZHj*;[bOz!GjUªZlƽvP>|G1}xZ.ѭ40_XT&`ؕ8K(<((4˳m(4Z0^~1eaK/CAXǞ|ފxg \n((` EyyGl4vB*SC1wOb4V-.rXٓVu]E8k̅bql铗b4gr0 g9n,PBA* SITrpnBWPQsAX |.bU^惑ʩ˼]aK3G Z^vHAL?LCUTUT5JX"݋ ~m99Z]hz{§PUkiͮbCS4†eCL[ls$W3t]/lpnsˢq1Hrz5C MġCqd)6+ vHv%/8'KgX|[} ЧF[vucU=CbXCLH4=/df|-"v-D@~z}}]hЕ V;]NӬ~뻔q;GMEޫtYxF.e՟fa vXa5by6\IjVdQ|ssg9zXa+"-I'1yI*y]Gs^f}_V \}8oTķUP[}NIhҶ`ZJcm5QA8#Ʒ"30C9$/,Gg$Iѐ$;kp"8 &okjWCDY|=8 |"V܉ hjq2[ :"V&!0D=> +>"zf~j΅R}Ň[oM)"V3h̒U eL>:09[!{LIdɩBns@85׏2psѨD.b5ϖ.:u99pqƩ>۽fct|2QnEgcMJTsiD8;,YVͤ'E#<>[{I^[X+/ay 2^D9cҔ$W Hb*ħs[O"ic$`ʒtkF$]EBW΍AO/GDc4vjdpYȢM@1W:c43CC!(A KJZ`i0. Wn\^]9 Il7*Kh[N" \4ͭ*;3L&8ՍiePK.]<~R n']sr=M:tq%QV5Gu~8=FvNݓE / mX f[G$4D>;zx PYt46(D~~b٧3H"RHi<,;5 8p؄\Hjui}t[Vl];ufV92ץŷy[Ԯ4-o];9ES"uޯea7kɐlJT 7}0(Y ȶ!գ~G^Xn"?cYoF9Lud7G t ڨg+kӒYW|UTzbeIHk}VPy lv9Mʞ]vw{G> iL:x]Gc0^ᅅVn~a+>4QU3Mۈfe7P6 -_L$:H^i*&(fQoԬv  >>:4\l7^@ݷ&c8o!KEtr- 'Y:R<{z{'oS_uC{ZovmYK&[.w*UBFhwY28ƕ*(`V ,֜#ѝbqaz X6YXZ`h?-Y7vT)41=gM l4":76VR2^&WS6IOa? o`ˤC2 @Ƚ[bS$ǐ̹}s?aPjt&$-T( m,/e*W`ݰfp{ ,s.6rE-` |&>*0C,>Zthe֕jA**`d*oN -#Od)a3oXU3E.c]V>5eY53p;؅s:.~M.`)%>v\&\fŰ+Uym3y02{ QFEâ[ictVXt#,AJsUsnOQdu~>J{>nPP*1#-?:m#w 5 LfaZWÜjA_EP9Qe{mn͝mޢnՃi?LԿ Iz%y1i>?sDtұkߥnxZ 4"ap> Cph`2R!D7J 'Xg@dAgfpf:pp_Z*F\|-:m֜=d~a~*cayjV{XQ7}qu-:V|y ׽ؠ!, _@A|ΑSd@JW;i,UyW5 |.0 g)6ʞ6Ӷ5݆$;ُ ͳ΃Cs`AOmO? T ?&GNC(c9"ek=`c8~]4N]?Chj66{5Mic apLK6$ykUmN )ma+!}ԇQ#!F1#80qEԕ]Vur?;mr)޼5t>];K-BzuR豊,>'н>==i'|p\Vjq3\ګ߷zojH{O{cgV?3#W pcyjhb퐎,8\p\pzaAxk C-̂Whsw=Kd[KdaAxd Ӎe^y#/UzH*kFHJ(EkOx%řKx[>4MҌT_MaN[-p]sVA 'hIT,4B!B[Vs$i x H]IeŭhV>FL8YZUˠˀnqE4yhSBTAЋ_^qQMy (Gl )2[f2>0"c{w'p x ayV0 A^l¶a26!` JT!p+ !ڋg~k ׃ۃkN7;굎:YU?;Wo2Fx=`1W3:TLW>#+1VzRkž xA 0G8 _hAk‹:~$gypU^onmmw`g /v,j6Lm!.lmIJPw1]AL0&N,FX5hC)\|ߨfdY_l?wD{.6u'G}h/Q:U#$g+!nY ?0p܌^`a_Ğh$E"xIq+Y&x&29mw_k)-w|ٔj_r7L[8Ğ066Vy|0{%g(ꏼ{?1"ǖ!pES.?7[@C-(,pI݀qm\~nhxn%\ GdP~ʽ 챦fX X1o_Y1Pfʧ˂q1,O3Zqq]S dh 39?X~+Qi"4-84~S"1M[л)`D_yUW㥪Y(q*ykV0\A񎒟]$ ݈51])B@Vkx`m`-fO7.<*|8.eUɻ;oDnU/RA~w=O64;-h}bID72bK?_t{Hp9yu̖UVo D[]bA ܠނ E>LtnǜDIAyHΟ!Ԝ5=?I.N 82s}HFZTZm6q4 N tirةiDi*ѩ85'k>aJ^}.K$Qͨk?g"DI'F%qRl[z(nBtqS`]$iJU(afy |-8zGvg'e꧴?%\),vUz\\k\sN\nk.Nk DTpG~H.ofL;zcr#{K}fE}ͩ\c=e5vsZA4PMHpT>|fK,(z}4x=9t1kUnVPħ Flvfi A$ x1`A^'98KѩX]c\YɂGMOk(̦h; fHMrtTGw?ο1js&vX`']>VV{@% ?:A \K_ζBҟL\RPFmRIRf;IxDً*B^Ӡ/%Tci>iJBM5)ܨ}EWuN.z.Vtͺ#LSp/,z=#N؅ZޯkﰥI^"IogGm$>iOzs!+i.\K*krP&Cg,Um}9-莅sij_}{-@LBj)4)sZf=@JDU=f4V1Y.bO']})Gs{~ty41ry-Q32cC.}LdDb-sPJ &uf!!Xa1:]&2 -ẘy.U'`⫋UE"z|"yiQ|]F䶋چ~)8)0/ZP.''-GmDiI |K-LSҦs:YO4QYj'oFY(#jiNmԬR* eR+#`bYP|ORM6`$Be0$l%fEZVEHDn9T_i̍:nN+8K̵45te94('SVrsHlqa}X}γ^fpnĘ0W+!e5!ւҀT~|j=C۱HMSa^?vc9 _efВmGR L =b0s/YZUMZU]Z5D _*+Y-N*%QfEKR: et=VĭJ BYc5&Wk0.:pO0:F!'pNYLw)`Zhs{Ccb*)&#.~`(⾣|U}6".ɚy3TmvOf=wjSe @-/;Ǿً,(P$c Im~Ƭ;SXLq [vTeO_^VlxEdU ckͷ*8R1t  L< Tp+KƒFF-1ˁ8iHH{~ NQ|7qDRT$Y4˛]N颔~_=Ʃv'Ýu5rg]'ğz>lML|t(}ǔD7||t?흼ޫ빼h0}sZAn"S0\}qP-ϻc'w>s ;8zwo~uުZ^t9G *)HZel}rfHX|56Fn^'g拇4΄@3Q$t:42NT}Pd 씫2VV;x °( ST ++iph>e6PQg^H>hS 'J &ouAzg aqj`A7""A ,+״0N`ѮwڹT{{N[|on?olwu*r`e _o$.|0!_8sy-ٌI ܡxYhmYR|,aUؒDŽR|8mG/{ h(0N Ԟ)I!P rWuw L9d6e\%5_Rc (Ir@R\  q:!X oXIXM0z:V*}sT!ZEN@gf3hW2g3$x4@HwK"xD"@בQŨX ^E=mz"ebAYl1$kBuY 4% }/[%`RWeQ QO&J[ObdhaMO[/MʘD\T1ǂn, y]rCkXj?Es˱ ~x~1"ߒxp}1.ӮA˞T* v\ՙ{$.2!@a1Qgf.8|E@h>J n'5B ikƐq F,pOD$,kS#18וѯtenɂ_:I=80_&0q1wCޙյ}8 :@PpTz(P05R޶vłjqt>, ҅ AOL~rqdZ+hAHpo ˰B `O9P§mQل\x0p gz0Fcr<֝} j=P$^\ö,C > ַ} Rσӑm4J`JS!.伐o/Sƕ1`W@rZ Y++0}r|x{?;?9|Өc_(mqD-`t9*5S>ģE.]&l'3Vg3w@p2fo43Jk$%PCISonmmw`g /%|"fLs6 Qwq]=0ǟ7[-s k,,. 졧I%y7JLl buگeݪV*"e[́UN]cIkt.F. Hkz51kPQ7M, uuO5/xulTwF|9ˣ"y)޼v]em>Ljsqf`$e=hN۠WИe%uy lSZeW`Ko#n,ݷmHj=i$- '3HUaYɶa4MrcVM)1K3:В(03X_f㑖vwvDQA7 ~L]^r%"^יU"̥/ ukӚ̪M[ȁ @4B2(`\` 7,f$l>s_SХ9- ti@9]s5y,s1>=}jCpN ) [AC)@0Xt Q}A7BIѲ_ɚ@_-ر$;䭥\n_7sn19 6^noY(#kimԬ< @Ypb!JX#@bŷIZLm/nO~PDΕ͎ϰT"?̜"y!N M٫Ear"ssTLEԩ U7k$>%׵'/^k\mCG\ґt8&p"Ut=y Z8lvq]X,H\-ȧb]L&<?|HR/G&[8i6b#bU_ZPG> nd"3c\Akl42q!KPQZ0;}rJ䝗\`HLJQ~!E!( pmINPk= Urp4[;'{6GkJe(RORI. %0[ij:_Clp;"$ /O&Q)>.kI-JgT2 䦱S3ѐA'@覆P=fdŃ<흜w`u5GϋV9l$r=[CgE1_[-b07/QfFfQۼ*]R 3 u\DI}3LOOF_23 V"CAx.Ի5X 4І'PiX!,|8~nO1HoLxq)_!;6-*6>7kT? OwDƷ|M).>pW\hLp!74.95ݱАҌ/Ǧ7K8l[8s?54vkH,`*/Q66ixe*ըKPVQ_+˨\j"uH;/Kj׿vLݹt%V=,Zovg/hON.)cs^3lJJ]yE7oZ8 88St7.*wg$s d^E)>ޖ{(D틦`q~*. )NEn]sc}毴b[LeY4Ι _%PjGlkp5U9^K!M帎_t|i-gue;dQo?:=(W|\r HR/tyĞG1 HRԍ1q[)ŷ_J?I*>@`i,@27 wDx_wg3Z5P4 @U.~U'zɽ҄nҨWPYƉ7PdQ\7LX.i+jC&wMd[HE?'7-u&S3>/]h]8*`f:q=18a@y]2f(&&Gk!`Få,$]Y]!`]ɗG̀r5<:Ɖ+++QZliٿ lM pqXr2 Mv{ouiNeP \O!b1,sMӞscK_x#=K(rpfb3܏ˢi& 7"OY߽7/Dۀq,ᛋp91s,9M('^IOn[?N^#`sQVͱk&Mc"s9=JMS?8jt8+V^ ?ĎO}`uRj'wjN\*F#0ϳK3ĕ.yda.JH'] alz9dr0l< 7Z%ki 'WQ+DKݤy$KxU*-]nnG[A~ƫ< S:M=liFWOZ8;5p7xR хkI,qK&. ٻwչyiL.pU/z9'*AtQ@ɓw|rIcT^9V =ȯ}AhKִ F1=hL4_.L 9`0HfuxY0m_^)TҔbA8 T٘ J7e&/wMTxc,S` p–<2#VYFm3gCQ(c~NtZ4Ϧ iZe-0j]ϕ1JQf("ٸ+Y Dyxi}B={L@KMU< e11HXBx2OZ~Roɍo-[;&.JFmai p8vsLјS:D`Y8 nfY4$'CQPq,0oy;ٰ@kj ۯZOjgcfQ@I ;"zfYb(64|7 c:˨y1x̓z{XPaN/I? x+D& Lg\'U#YHrrz㋣'!Tp0CwO8٘Ii:;EЩכ[s{ڤ_P!Kj6VBϽ 7w6vJhhX mB+>eYugMNaxVFFꙜLEfBKNfE湑+9-6v>lȰ^ZB!⋄|:$yxǎS#!;0,yXqXTX]H-P"> a@M淒=، [Ʌ%jvݑo>yCXI6 6r`|^hi0m> ?i=x"vb,\rRE@ _A#IK^D\[LMD7H44TtƷ\Z}{3bREl"FvJrK>p _0߱p;]J\L, 1I'pzw?{Ƒw1K\lYy01g1g7&GGHcP,$" cn2{N֌oU29#p;VʿQ¤Х&QRV>dGR`~iIh$&B;ilYZej/ )i]UoJKlKs+%a:$3sd@<=@ &=hD{'Wr`g&HJg*siR9M4ͫB1' K3GCrj@$}ӽ!e=yA& cil" 놷+#H LܖqʐːBƀXWZO*mt؜>"y] +i}@Cأ=I$X?y=|E7߀xz{W-LXA^C>X^jLӪb;:Q5KoR1A*RB1-RWajCO'P< 9dU,z5/Ɍxi5\RL a=uzˈ$@w8GP9k$ˁ_|Lo;k;r;c&f#~&vH(uB~>)uڢ>%O/=^`va&wm`I<;IQ:Mycš$;ƻ2G@dO1 n52DOnR86H~ILҀDKb˺v# ЭLbe"/n&RۙH*|nh"Ug:^u4w=݌VV v4'4WD0Q}SUUj{ב't7q@1քvC:GB'>wW _*T|ԓjB2p T}-)}ȇt?c 9#wG[{e{--6x݉(>e+q(L2;ƚƿn:cY-Y~_@Atj.FdX\RWL3S9lli=C15xfΔ:X߰r|t b}JpgdMOS2N5rq)ǂڔN>g2ۻY̘6H!hf':w;} Gjg`vt}M*ru3%"q]%sYERQiN\gEqN2Z1,]sjZN="9edS\'Ρ`)%j'ǪK ~-B ,8!n RB5 '0{jx y 0lchS$&-_pr3Uyy-uAKUr`+4߬#gIjT%9a|!=_6`sJ#:–Dy=@;dz6!zWd\lc9[B#plq23S6HZ^ŰjUk|Y^=<say|Vc\pZގsos ʉh" dɱHf-ĬX77sY`pEMjVQv-㈉߰0Y<)4A0D rSn\ Oh .GT}?!ͧ|[P3fxw@foqUtK Q<&~x;b}>,㽃U, |DcYAPڤ̦CqRVj²+BNR]4Dh5To *Pk#Bpg\Z'fqd&-d s(B <[< w\~*TH_l WkBq^?Y[pV88J?O^ Pdt۽&+߁4 d `*~bArIqoܑ8 (Ӗ1e ,;S򟏙Gj:KĴ5}ILE?z=?rpD6%e36rNs2K5?0>Q ^v Lsk?]@OΠe7w6u ²MύHӁl^|'y&2 ķ_3'~kuscu4,{ؤ4iQFWd9I]Pw ;ҭ ٷRАKkY hw5;oB ᄍ뤨R"lC͙AVw SN ^R-FFͩShX -lV RXzIL׌_ GJ,(:HP{DZ z:g~"!kfƝs0e%r}8+ -Ny5k"UE22*)(TBr),,"VȞyOXE&ߦ&&&R)*4'&򅗭wOɿ(+v,H.v+HX2ιjf* 9OE*"W[e支}ԼrH_-I&"rd/*T65hGJ͉8nkdzJ'GU]iiWX-$0]>-3DXƲMuD,h\Z'&yDbFкY8/hk<s_;7S~q+VW_`_Kaa avǷd|% |fڲ W Z ՓwIo*/6R.9_+f9j;=>W"2i.u>[WW!=JEnA]IxH &Xw)&pG П*#r/0YiwQ[ȉ9X1olGF˒&C;;jo{vF㣿d05na6Xx\u?XWOqH-`6%b sr0Q|F w ؓ1Re Ma'r&k~ñc )f(#46^nmQZff{ɴޢ{35oLO(w%8PE^Cx"J[Ui: ֲ_ײw;)B)N/ݳh0͆tx)\96ch_;.QǓR~'1ލoF}mJQ=̵{G |_:S _Z^YNT;Kgw*vo[X$cCv^K? 7F.$Wf\k .l++wɵVe%6IKtײ#Ѿ2^Y˽FֈߧP'HU{tewa^"TIsZnP/(vfJSigPzDCv{'iy֘͊ΝVH*RC$:`e\V;bU8k!vȪj{U&\d RO]d9E yeŜ0,&03pa"B#R+b"g-3D/-e{j״b̨jGvD?+}j_E3Ek^[L2>QQȺjhMl"4>n"w8{8/\5A8b00o&d,z{pWii:_iD`I7h^dqg{+jC@ym9<^ήɴi wǨu%ӊt۞O``D_?ZLJo\v-+=]ks$|nǥ$.fQ.=y}Lȩ!m>!SGHؾ5[kؾEMV1Gc8#ͯl,f_IjEav,76CZviM Țc;df7 퇍,{pSi:2[`_MUY%ؐM:FO2vSxVP_**#MY6B}}]2#PSׅ ޭ'^:ז5b'ӼXMuڗg,dQn{Pe _O {ZÞkfFA]C9SKOe|cD*OA(oܩ۹.D 7>ڊ%Gamg~ ܁*pF LWL7Dw/x_Mx9%u9sxFR2u :__rPU毁,|c#GD%|X>p+kiT2b+튻#|_%8<=2MсCWmaI5TXT#О!f>0agnpzBM5UƯQy܉߳M[,踐+5R4ۦß0kn6&J^xQpUE<~R8Jol9Ο۴QT7G8{{Vv0 FD10ۭTUDt0w=ۜO]G5BwW ]+aIzNT7#ա~ʗו6לÖVMTmmS8lV9?*9L㊙vHŜުF++{o98Cuqf/Lr)MpyLL y&&$[.בx ' \s85y m_㻣{;X̙,J^:%yzf<Eue0kV䮳/x5h+*vyl@Q"NN%5C5|I2X@pO]:>96{y(+R܇7#$n u#d gDKhV#\+- @4U)Uǚtf'3-/%x*fG1#QQx3?H7k;* ڷ!.F2|ѱL)+tOx4sD?O%pC%'\/,HS(S6I5w|V%RQ?ݗWe.1Ax`~I=yZx9Bȃ_S΢=9`sBpqH)?IRV U`/4f 72]AqP)@6hI%o ef6v Bˠ8.X(!t()][)\AhWSI-pѳ|s@Sɾpp18WA5 *vE snx4KU te.3O?Չ+& h^m9B5d@MrRvgYc"-Wt Q ㋇r)4)K8Lrܜl7'r#?o-]c?}I(Ԕ|>ʩl]뎲]J;p rƄj6dۦj^|Af.ʩM`;'Og]JӕCι׽doKB̉+é]d`.\l_ږuѨ͐UIc-P AsUlLoXPZ4;e;ʫd3-ưM+EX\ ;l r^ y%PV9$2͊"1JedLai l~-uRZo~j`;wBZQF REW M+=6+f]S} >fq|? xv,jMFm@u~+TvƧ^lkJVN5/˜eY32~FS{^9+l K3oҫ #En8H{ %=bD. n$T/ul  Vame-BiW4vi$΂k'MBwkvo4Di"AWB\& sx7o=w9Ն9 i6Z>ɶ7,%0$Cd-0ħ<,)q/[Pm  2"<.{pRM.@';{w+xfR*jiU3*Vut$B_i#z@V0Ǧżq*Y{,34zXyT~W>8Piȥ9`3Qۭoe\ o7,&69Okcf?m;3 :V9Ydupy\ia)oŴ52Bdz)? 9iXI64u[|X>'2Vs_/t;'_ZZc _xˏy8bT$dGpC1l8wԀ_2>_{&AoOȒ(h@`V|vȿ[GD/o;CK[dW__x t:=jg^ 㿯ecSdx_E#M'M;'G>|hmp6T;Pf8! ,~68 aC4ĉWfUE <ύ笏GoWEZb2"8dG4#4ʂ7ѧ:Q)^Sy΢;ߙy'8{>5WށhCO-LV .b /SYEgT#=>U05GQ/d(Mtf9 y6>>7<8ՊQ/h\"&vʄZ-el,WB5X4ޙ,큼b(nw- p\PAurG,`\A^STR rz𕰥fpƽ 7gn UOT,I #uYI#ClJ+ [?AyA}ծWw]PPsXA v+̧b?~gPfdvwX`V7De%wۭW*PrH)b!|PTA**Lv9-L?&U"=l3W$]X?E0r'UJ)sl7-""w{+-˜CJ%+I]'\,T ߧ`QoxgQEp+,jH4Nإ% azsΠp z `}PET >杆i\r~ljxg;8 >S[4&Elexfc94(tL]E4sJl0E"6I v_>m5ZW/7v`9==gϛ-J欅usee¦]<CDJ٢m{y_c( ŲR+G&*pl $`t]yc#Ok'W16+pEYLzOQuFKD*FRL"ĕkHԻXkcV~j<H^֔ť06W^x[JV݊D*-)BGYY0(K903Rםe@OM%y01EƍÿbpNXCOSu ;ϗ̜ux_WahYٰhqjŢEѩcthh,$BB斋Z,faņPt K}OP|-mgp~A>K,[uua("fxa#;܅1Y=0~fs>/@inLD$5L il7=!FpPpcꘆjc5vu膞膌_<*58xBawY|b:Ժ bNnZxB3`D t}"'`N!bP0ѧ,L H\psʒPCJG^s;㠭}(jdi^ܧ sC#fH)d^1;p@nތr#$fq؋[xP)׫tJX mF?8?J GN :`RPޣꐂ ,`XG# tM+j<^cֱ?1XcT=h:`{=m=Ѓ⋈ss\q@L (S $?$' q S+<~W%jKyVbqu丬WG3W"yT߱lRGUIKxue(֎?e0`[؃%ۢ#GpE4:#[(3˗{ZFeq6^mĐua_XU/n?6UȀnnm?eް(pO?Jn<͍4WpO*yf&|[ +Wg(uO'枣QGV.|nv Mf88쁺,Y5"= ;WԸ=5ngJ5Z"z ;CcD&Ӂ9!fZc5`)X͔ٽ#Ǹ>198"pp=44qeP:J)#3*;HU`n3u3'Gq,S)h*liM#x1固l~mO4f bBј*t~=i:`ĮwC~3fd䏯TћSu0Wr. ѐmmN04yak] QW]}~އS 4EUԧ$frmEF( QQ)[)"zٶ;Ec>c,BciZ/xB!;gt H qįgnʭti4Rο:/hpQSDA4 jLVQiU `<]S&3rVֆ ıylxxq{ ڀ17dS bnc6=1mD_ajpe _e(20O0;ivEx@W%93aM]Cv[ udp'xX0u l.d%#1ϧQcLQ4*n& w'xPy! (ke`޺p!]k~)D>D03UpNz(;yO&6eylKj5$7qxFD ėT0RZi!pH BB6 Ց SC~~o>Oᓲ?ܙY'I*I"IVj>I$0}f$ToSs:"w(a1e@ ug)#jj})-[oG P6ϖxk-_2Muh*cPAywb1MloXҶ'gp[@rHH1Ajn lhډ yТiyςf B$.FbleӀBw4e zz_CАŮtzcf rwh|2N [H1 SS x_ 6H$aoD7Ha L(AWޚ@I$B(Bȁ5` @5{xե,O?\t:۝Ӄm}lzie6`w"F@lݳ36BMy`m [#({"ؕ1{Y,}nCw%ט6fŸ355Sa)͌|N|d ^za/ u4!Pz/ ؓik_5KKKKC۫Iy"G%xYxH tTg*gJ;gsC1)(}+hB*nOn(@@!w y+#{?3-OrǷ! 8Yw0Thxdus*6jl-{A)OdG F5-;"}*Hq_E#M'YC ,;h8^ag)'<Ů, Hʁy|N6 pBx/ͬa9è@S'7bqrk*auʯnwisŖpU~FW[5OűQ#"+Wr2 NVЂe<#lٮnwPQk8&wGd\HOi|{i3~]BQcB%7q{OvJ<<*'(ţ(Et ž}މV~ؒ}Nw:߳kԗ.rTLB_8'PpcjǢo.dһאE%AA)4r|;5?5g('![0Q# f FC.$sQԯo3ݰ{1ƝsUN=zZuVsUue#9P^WO@s Z:7 EB qch a(FHa_x6LN9-?x StUk&h?iVN77Pڡ25g :e3p`Fr[ztoOt8IFMD{NVMr֝\7tx39 !2o*Vj\߸jvVPc3$CeJ MTRaW'Vb.5sn=/UuP@^y*#Qkp L-B#Vh{!U:uGx/HsR{ҭ#&V5g񕜔2ZڌN BʪCb{Sݞ?Rj֫vu (731SCGe'0)dOoM(bRjP8QĨ|8]}3++BiaRH>ioznz g0l~vl|}[ě@?;Т o) { F `4 C+$^\09{_63P W#k5+c@ 7v/2f̧(o7^y)mblBPH6^J[RK+MK[_:}rۉ`t KiE %ԣ H&\t"vz1$RxN?M㋕:v{mǂ h<ۜsݐkt?$b3m7$]k'm̍Øc싻5E&HA$(Ty4l^NxnT_8uS ҋ4qN1r}PHΝo= :Οc>f{q"ъm`_vAJ6i=ܶ ܵG;/㽿޷A(]o4i> +H@604g.@OqW^ D$$&P9t={pU$)}" azB.:}ӷA 3Bv/1>QeL7:κt&On%; |v:o2GiNJGzЋypސU(fo@XX,e=}P^iŽjÚδ!/`o| ׎Ѷv;LG[uUqȾu<ˉ ۓcOvQc;ݖ8}/] K'7Y-\Sa VWAX%R0~4A\̣A¶Xpѷl-#w,o/~4y4hMdER;MWw atojb.˺!Oh[䐲[S# 5/l/nX3PfB~3H(9(r2'X|0ϯ,fʵ{2A<Lln8y,% R6aNOxLF#*ZލG׫Y˦ch,j:?m ORXy0]b=t{fFHu>]S먂FnB^wH+T̶Vm^C=cdŴV&h 'U-h~sIIWabP6ݒ?y|CF,x@h4V'wNvޞzV@=T`\c^+Gʣ"f޺f܌+iǵk\K]=;IJ z{Xx0 ɯv[w\Hߩꚨ8#Ey^%oE uR Qsds.#_%WhXQlZw$v#hߑ7y59_տ]3ޑ1`v-S9d,.9 nL+V~>i. 0 .PBȶ6t.}H5Mgrp!+/4Lٶ3"'JO ~1n9" \zsh~Lqbo1=3o12.%GDntA)Ʋx]ؼCug 3ujG?=N}ܐVRqCS)mH"Rv5妞Xquް .r/9QsrN\H%].US"orr7P!RE#m+nc' \; Rbw`a;(Ms辻DcWHB?1c;_ïZ.m$J$)FKUҘSԸeiyj9>:3H^x/"/wZg[?}w-ӭIWsi<:K=P'uћtW3ͣ/R*/)H:ҫ ߇|D 5Jk7~ՏQݴw샣Iv4~ 7ԭ~*.'sz _!-.c4$]KQtQO}+x]`ԡA.Vu<^p@om2\d\aRofX$ta}.(3vCyIx3KK= X$l1i`y' | 3"y>]x ~֝*rӥ% #l-IӚ/x5 }(U7\őb;"bhn>k@g 2lk7O,~F; ?RȎ*=G0L]L@0=ښꕱX426W/7vЉXYMgϛ;n4.$̙ qq> BMs;9epO Josy?h*+APmJg+kJ Qq11L5mGk_E;ȼF2ւ01@{e _Y3It;&61Ŋ!ҎM5i_P_?j Պ4\ϼ< R)Bѣ_xdҔH1DELIMLJH`!Dت`TXD5=s,!LPc؝ ZN[bxovYc&^eLSf11^OyZTdXZu4zןkCf0~"A!@:?n{:Y ,;҇?7zZ04\o/ܱk|}/-sB"k|knD{!CˀFh$l47 Ô?sX(X*p{'Ng| K /ebǤhJ=陮v*rst||Hv" ϨJmV^9֌w.]o<aVۆS\K. 7'Qxk S-9q`OE5]3:t~u,ʐ1E wB]^䳞ua6ESC"zXpRjbօt7kJ"|8js-$"hzUG'֏wߣ/2mq3ϳ ńVF=iO_#/秊#v @ 8J elUU{D/J܅%޼_duN_\*twu¨q@|ě/d l^[ ; l6nq[ұjP|F^lo/$Nm K@,CHE JT!*gV$xo*?YA,g;ɷc7!D;7>C^? =%ZFzJwr$NszzI0̤@SlMnF#0?"'WF6ˇ#E :d@Q:%7! yx=LHz)b(V'b4U޸.s :{^E='͈"Lja9T:<*;Tri:~ibS%LT,KtK.׬u+ ,P%7Zݟ $9]u<J ۵̭4Kdh,ZM)wS#y䜔Z8W tpJN.JfkڔM_4H4Iz}zEm|N-zOF ͝+/66^Bϓ 56nz =-Тgͦ#ʹ҄ ْů&+h;aWI)ue1'kSfӧg ò/ _If#DfCF^If3D9JFV9>k>f$"S|./ GJ@318b>m0^p-_fxi F{&|Fd0L#ƎԿ %{V̬N7?TU#\٢>gY*op'Qi ;9Wk'( Z*~mOԀesS_]P%ƚJ❹,[m~+N%{)t]wZ/_A U^oqŏ?֌eGXF4Ia?5g%Fi:a9!'W.o7mfIj)jJE|*_!y트 %HTP7i \dv7Gxu,./!eI`Q. f.BT^Rs;VY:e'}V<+qcuC N7#=*nW.H5]UuUV1Q~1u ^dE"&ƔiF)yaХ UUgӈCr'li&ƢfƔvLAi//J'ɅYƆ+ٰnx¹|]JeS䷸)dچ,ZYf&8A-oE Zx}@8Y4Cdp-c<n5*`,dXA = +52^xY;KCM*f4H²Py#P9H~J.g7.)ߓ2 &>?^qp?JE*uL"-9?}yQbcN[b"m/d-RH*|v"Uv"g:^u4wMǍVV v4ѫi0l2SSrqvssfwUpsuU8ۗMZD[VIA,O?Fo?vYd{:zmZcm, hݳ3X> $5WCd,Q< M8חNEAngV$'gd-}L8Ǻ|U ,ӫ3M6:ӫ0O{?Ǻix*0*L㫲8>(^DI67N1S: ^ʭO͗6L. +DJ? /*طR4T.՟W+u{:ZI#N(d&q=jFs+p,*cb3 i8&տwx4rh`t}33">H9\\~V61p3B6[ B0c:UNJsQO;M-Dϫn3 K28+nkE&x :W!'DQaExoa Zyy,auQMqMSRFJ?K dvd( 2U|nO(sU&ʟJ}ek;KmI2?xT3U?" ~8"-O]Z$?{P'8&̈́x00LP!q6`U? ھ?gݛ.ॣuyEyOLE-ހ[? 0 TrnLQ[4,D=q/L7RQ\ڐwL+΁!!{pO?M㋕:vM|K2)E|=#B002)5c0@z ]2Hۻ6Ot=4[)5DPӿTOI.=%:4-"?fZ`zZt<>_n :$:wL/~f oy~nV,n*{;׮ J!YB-W{I?>y1);C~53#|҅HnC*;(Ia4Od0?n9 /'rB,I-++@a &8g7'e>eh fق ƽِ @+O]%P4Du[~VF﷬82[~O2xYsT. ;˼9|jZDox1% &@5e;.T_?B_e><] Б7>ԡa e \C]N0tNck4 4.Pul||x=^w3F;X]fۤ"!Jΐ)!2en;=kZ&l5uQ?> w^pLS md2?dy0ޑ>3A#7).Z4ED 95{.y%[{oWMGiW[Αf6);8t6t/r>xBrU >rHlY3>O|w3bc7 ?+_oSswUeTܙ3V,ۇk0aӗsN`M)wpJj`7[`o3l_v{/ھэ"'+"_GaS(6g[;,~vNc?! )Hۖ Oa+bg[G6ID| a!K5B l/Wj[aA!{&?FduZ$A}k _ms]wx^'A,Q#e5( ;Dh`n|QoMɯNo^=x;X%IĊ.{Ee%Iv)܇ncd=8rѸe έM8 g)Y8~sY93Y 1Q;RX ͦN#aDZKy1~+yウ;-9:c6*ɸ4rxe.w ns_.aaj--_4mVU,7LV풑+eHпmrO`vn'Y@d&IP{RkPeAhLqJ~=,h߁5U藢 HCĘVpi5l<>0To V|H/UuK" m$S؊ѯ?{#ftK|qݏ#ֿnV$xpoH:E߆T;B wX7J"@nHPnHLB HsN t!%8 !QJXC*E`x<|z0]BL{=f{ ?La*ג# E@f)^E‡4')B7 <]Jni{Ia}$/ y15>XV9G5h6x5if AZ 4kJ*԰Ej*iMոEѼEHͣ.Z98Pu=uoL5BdGaA5P"_ <>^aiŁOelŶz#&x'A xыa9r$~@ =Ø$'o*q_**ўxHrA sp2r(P料D iʙYBS%l}'X1fId/\&Iegf DxFs˰&Z a!% ² HiҖH=ҿ&/ j7hd2g?oG*~~5{Y3Twh!Thw8JSGI#T,HFb:l ^mV-&1M2w;cq4g](dSQ10t=g,R)Nҗym s |i`aiّ~Z(Pzhx6G }Tg#[E4 1"k״u3ljϛ^{H3vvx>By{f1[jH,Uƶy a&$_d_,puM,}{AE҆XwAp(+U07Ѵ[ #P@I_{*XOgp}yDAfQedR&2YCUbd@&]:5`aXY7f(#GF` orh { g6PL.#A(NU:OnmYyEg 2DP:+J?W=du9yaxo츍{vLɯuUn2kN: :@ET$ޅ>zb]D\8[;0wb_㽠 kQ,BWYbG2@!Q!A^&CSq]uRC0 /Lo{HZ?]kENK3N4+;o<, n?\գBe;$p}HɶPa ߞy'3)7Ƨ)->M+Rܵ"5rO*OIĜ[fv])ovt[­ )9ELh&^]j/rT|f܃/jaZK\o[Ã#FXVRc:%ǽ蹮h@Tbe5rjb(Evv*䴑xpQɋ Rj qL^$+ y.@t>Y^ }m^` /Z,,0K҃iN[gwū'S>JHjXj~CХ:-'yݫǥxӝ;UE9Gg ƿsD}g>Cc>d2Ì+&'{NN.K P܇7#$2S?_>=hll6^>f6OF ͝+/66^Bϓ 56nz =-Тgͦ#҄p-n }5a^wҧ%ZnޢQ{2-Lʜ}̚4(w4+hSsS yf$"cUCbKIf;DFT!%+5'TULGFͨ];TCfLf')b|P-uGhLR-u&\/ZD 6ͽ4o`lV>TYHHo4뛷ә)C;3б囝 D+.~3xW-mlįȉ޹[eBJ1i޴_F'g7Sst4cG#uyVG4?d \4:^z!ZQěo]|) )xrh{¦ Mawƺ"cAUn]5”M5wk|R=ZѹY#opGoz{TexG :YzUQ=|ׁcl,#:S"XW"fՓo@BO<ڻ/Txv65ܱp{jO'ذ7Kߏ_,K>nw|_xZEB%Z)w_%]vs^= ;Ql]Pdx '59@a̭|d1Dǖ19y> @r"Xwp9p\giw''rX2GN,|#ҲS`8qu.w4(6:|4K 9[3rќ}yIC{_Vf:cgLڭ7?e H_|]o,j]e' f bQ&cKCktR,OaJy[.e*.HLz>cuTL)ҥA#yU5Uu ݰص޺t# 0n/I7yZe?RT+Wqj\sDLɬ!$HFz3 ^z-d|3 D( R+; aH-Y̚ &<3wI 26c;PgcP ^J[$06{ofX1t{G񏞂~n!mB{Z_hv3,}c(6da'<8DI=e yҌ; 3G PYSQ74u[++ҊjXFm&FJH U'>wO!SiyH+ZRdiHʃ؆Q$|#Tx38PwA^q6)EiqTC!]+8̨FWGV>)X]6[к*O,9nG *4+Ow6Ջ'w҇vNJwώfT\;@r6l`"V'#:/ʊp .;ع#?81H M_ I/2\RD לW52^e~ 7 b#'=//RI%-uBI4UKޗ%p${ݠ8G.`=x Zv2xEl+<["kS*ߧ|seF=:nuj~ILc~=%!Ei6y$nd|8Q6H'Ks^b<;w}cLy:vћ2 XxA7(C G1fB=Tcn7:{w+t3b)*QGv; դřW4]jJ2-~]PA Wj85A ÎOM$VLsJĐWl<*ޔcHŸ$Qgv7ƗM)Tow[cHc5R+]# j)OFʹ;𼛛*5sx;v^Ԩ4LJ3/#jTFQijT?:* B膄:jͮ#8U n+N0!ڼѦz "8 B6PKR8h b^]Fg5#QHAekL#T㔉q:HX:pCX9kBfyJ3%F`#ngyzc? #a3߶ ;xm1NċX#^xriQ@G;-LJ$NQ_˕V:H81(0m=9lNi2VdHelbtiLN5yO^A CaR|;?ew;)4L UKݗv^бwG0g{aF>0 $qB~1Py_gsk⢆HH!c"e`H5[PKoWݑ.T#]8.jOLO ~Q_5E ~Q_T݇"j `/j=Hlq ~R ~aVZp(k j?=wGgg>D!5AKKƃ>DqQ\ 6Qod7d?^|83;ߍIuw| '|I\À6"3fmt6<pJJl:%au+̃Ϳòx<dy? ]#r?eɘto,f"u$g^sp6U{h9wi;0|q`t}3߶|$`>t@nW}&Eӂqzblkl8EFz!(?(o9}R_0pxzB%GTCek6%{ k6$ݝ(©m#=0;S $i#痎'⌋2jb,?pO\XIa [>dٽހ@U=-S`(ZؕLys5uw:bsBpW(P3~4 *2ύ5Oko20nuUOE»").[ v +nfOhodm?l>W H$߀vʘ݈-1We2OeOfWO֙:٣ˮޓ1FW&$%l'[wٿ{" ZP#vf{ ?&$_|qz2^>%=?~fEɓl8uP+Z0y 'ݾFc?TWZq;BZ2R$xAAe).)N'vML8;>:eޞbpH[TGԹ67ow3DE4zߢռ`ԫ.V!$džy~ȅ?t@ b}; obj6yV>G&VUE QJ60jOlE;s\p|u"j;W.Ě"ʆ8SR4GsY}Y]|-5LE&ȃzv6z&3i (!Lz(qoDYIfA;"_V=p-F6$wė5r]@GWz5'|!^R>h^P$'d|=~pB8P-UrXi[E~ Ѵ 6Tc0"Bg"%ԗs/9wYG.RRP7\jUU1E@/[1/\M,wU=Q@e=}~#ϯ0{}o\dEj_$*[$jᮛ^W%?DYDe%@(맃ZBE@txkr-r|_!Z w.jQoq퀬P ٞXk؇ӟ RViO@ (mTOܑIO8F7Wo+X'aky c$vhL*.R5.4d,a^YnQi?*`;<j/Nj s{seLQ F%畃%Fg M|RRYZ4zQx0aw&s6_^@J:uH4|<88u"+GD"F$W=o>lo~ÝT2 X/K f:V޵\Y<M8\|VHrf I !Wm;^ClK%T*JuQv]VSF6P+x%! * `(R ׸얄P;8@-= [M  db1+Xt6C\"%B ՛o\N)O;ٜBvN2,GA_L#>xC@)r xb%lSh`;)V5HG &ƣĞWL\|,HhMҏjX6%9,=APQr{G9%dNT HPBud z PL ^e I<dÕ='>bN"*ߌtE"ݓWJQvKP<J&M$؀ʟ2U7SY;=h?bH3W=uTr-8Pé!BD5;4:Wz*J++NWCZ]N*ܪ0+;c; Al0JLsfxFHL-CCU[TWʏ1(}sVnRjQkb6+2hbnd G<5`%Se^\ j#C9m'%`Cϟx5DR$ovs"Oѧ Ī/-aO@G⃥h뿡-@KȚ8~CK F=S$.௣ZSsR;i]:$d--/RY*o&ئ|pI ''r[~3{|NAfHF pHͭu'xpNָO1e_ړ08^5j+,f+Xu\kjAc Kc$7($8@Oͨ4@ ̀P _GiݮڜD O\]ZzD'8F 0\n ,@nZWYyt8W 鵎J1\+jߠ>WESpSuj2܏2 'i܋Yl:z9PZB2O&t7S0r@l{c0qtlݱsK҈3n:vl ċMdKYhJ^1@1̷,;wbhzɗd@ 6yXEwɘyvןo蓑"uJb]4CϔxcB;<7OƉqdaD?F51l0 _AtFEȧpHV#|pP˽Cb,ޱ+}uG>%Lo/:gyqU'n'A",U_6rj^ң'姫]Fz&`b{~VR6\HV\֋{($N(Kc9s.mǵ6 l}5sF/[gdiN8ȹA*(;h7]; Bl$`T?`Cq#+)t6V@dd^ɠ2{V,ϝ>0#h'>{˃"ljl4ʊ.p/`ld-za7'o@eٌZ)Bi'bR-:B8F8{9|Վ,{8jv+#WRѯP5tΓnXBdm)HǒS(qPr$ (> ~=;*9 |{ ya0S9XI>mcN5P}:I( Q]Uћ 1nj_wPq2(Ew.]tg4TDEx ^0}'i}Zz`s> [st=jCuI~F[P-g4fƽ3Φ)Gg @%$AݽV}ު7~k+.iC5V9<{3q?ۭu_t;~Nۉz1Gp=[ .lCZtnaNtIñE|:gu(q,f# %8(qjVж E3:ӎNȱg`ҙjGrNEIlz_yȖ`;lR3ڤC<^wbu nF# 3g^BwE-݉zQ򥍄|m`-$)`5`ǫIz $+.P$lBDH>^j/=̬ޱ:ia!1piD 1YŇT8V!JK4 8,Ivnk*ȗiiW lUR"@X&D'sM)Q Rs a&u6[[k|"e>t,^4/D]чq?WfSYи`ea_9 O+BnN\ƺw|\Gпjǧ䯓S,A7GG$,_~;a| Alٝ.ǰ׃.N~ ؈ll/%6xEǷ+r2;WG[Qso:ԣ_6S^w.ɓ Z 0ӒbdQV0,e#Mnn~Z3KK/e$ bK,̴͌(QF( 7Ie !mjy,1X?!> $j E 8`^!LtÎ֢+zǐ۽E0/NtOMtsv{^PtapZ5ڤYc;9vl]/1B)h՝@~G%іւ:kb$$Kw%#WDvH K@Л@= l@AxraTڏztSx㌸xN2 @FP#q+֘6?z" SB ͝ aAƒ#9DN4߯@y03$fb~'AH9S3"+ٮ5" ^/3JPMF`@0J0!"gvaz>(CP5ED'=%,1XR뮭E;Ymӷ\k̞PQwv8Rىp/Zz.L$lTZN=i [M Eri~%ž|#̀z)69ǮcŒۧeQ C12T2pd3[f7D.P1=rz;tơKFE#,1~ aP^)Xz qct@aE:H(y "(x,w,m^+iRPDgO<^(`1(3i<?xgc$#yYz;8<\:qgpy(!?ͣ0OqeYC_qH6!s\~%71 ڲ/ ~]C\ٚ"4WIt_җtpK!@[`=cggdtQGmfw`E֯R{j)dB,BhPw7([P FӋO#gЄ|2hzVÊ{٠xJ6""wXWP0R~j(J%(U°%Y &VT]LmX!EN)" }AvR3O.eخW첕d5ݒ'!/k.󴛎dMyBN ߧ$Wk?/i"g@v:KZ/5ArpX[.z!ҕK,6 dUr) G<` lq!ǢWjuY%P-zCP&\z)fpkvɘjȮi }p~'}4UjO˸jhCzeGIov`z4V\+Q q㪰~V-x>m9H=MOM:`N_ >qտ^in&]TSYd3='VϘZԴڪsKuQp7jm3KT<ݎW4yv2=qj=1SҒ(Hv_PW;5l|O9VZVnji&w }i ~erқtcQΫ9/48wU- jW;lM4O/||DzF 2Je G."0gqT^R3Lu6H;eֱf)> $y$Z[nK- Ve%l~ii??Zo]]^,1tO٠UȬ1Sm)چ5y4m*x4g ؍;\"aL"IE^r }ɸ}eA1Ծ40.Y镸;+,ѣ RiCZ;%$sNT@$A/RbF8{iz[W^E'gg𧻓fE<[lѬH2DFMt- >u7X6x%ܻgK?G^'qK8̯F%Pf Nȉ hG̋A%?mTa$;Рk z(MSSƳ!tx8f sm*\ 7G ЩRR:QeBWF׆ԇ9٫Yɧ[;VI`JIS0s 2o8Yړ!)av=db'qŧ(&qdYLq~N[T"ܘ=JqҦ^]UM;݉|ܬOa<~Kp>>S{7z `t%)|tu;Jq7ڧhgkt,f(ZbEK6ȣ(%V*Y,W BB?rrI0AWocl4QTiaүhH 'ǩr u3 EuON؇CnI;%.M M՚!`{6R6yT!Zcf2_4-Qpm/͎{҃?8|kqӇs6/2/-;~XleHrDmY@T{lQO{ $ߐGA_8ք[Pӻ54˴&VLag\̬w1#KqlO6!\X*^6Wٮ۬Vdה+>ؚ>8+)=K@ |oSj:a/m(r1ډ;Έ:FwW'+ڌXG1mG а 5]v AK㩁tF0&ӃL7B3㿩g@gXMO lNGdfAZI֒f1+|4ޜk/J~eƠ5~ݨoObAx9ژyQkUa #&CwBNe[j&tV/Od4j4?ם , EN\'[,3Zo}:}dAZ_VL3Nh66Ym0Ϥ=7K)u$:ǒuwA s M)zqUD#5&oD&[*66ǕP:K?d08խTΥTQ JRWєQTuwu'DZ{֠F;`X*3 >*[*^zGex@cM`Vm+[e\ Ek(s%byrOȤH:B/ˌ 3m5+ɄٌgMsh47/",e ׃+ˮDsI~H#f5J搪S>׷#f:[%5#$V'+Z[s-oS-A,~K-=2@/ ٖ]~q]kK?7ud,Gv@ ~_q4D./B;'r>*+lP;aZJ8,B Ps6?ps/?\oWi)메a+|v[DFsaI^M}H +D1:gXC9C O?JGy|5Oaut"hdL|5]n0/7 SP啋+s*Aq1ykYAAb\s4(۵yeeI+OG%HM{RoR\Þ"Or(nfB*TFC|]3Ew>wVN_9o@玿m#\\mB#ކvV zU?Kp1Hh5b.ؠv<UI^,\>bSA\`?g}I(.mȣIϼ Ny'BDOȔ,R~QcѪYgSWĝ꡵J HZҾv₰RQp"R6023"IX bn٢1Bn&FOcG+WJt'1C '*vnf}M@k%-7֎'# ѥuyG]LzXk_`R,F)9HKқ\SV8B S*{ #*R4#ʲ Lk 65q;;BprFkwU=;1Tz`BC!>Z d;nӓ=zO+XvL>FzfAf X>s$xLo "F <@ c,ٗV֢$`$} *&0|KjѦ_H,7pZ$%)1Y͜jsϠ!O6fl0Xߺ=<^:V#8*üouQeZA΃Hes1V@"*&eX2bG.q瘉J!tD* {^da YX,A kYXgm!X,L@& k"P؅SiGܱZؔɦa^C3fӞe ?}]D*'4 q:VF(fQ¦¦*0dYx/ YBIkQhdD]d4)XK(YuAƥ|)V8O_v4_Ԓ=ZS9y7|8~Ւ}ß? 0'KN픋ؒԊх)x"ܺa^٢MX+B6kg*!&!S˜fqځd ˫ \3ӘC%G3z܈܈>M$ѿ-A 6=L1aXhg‚rՊ;0 3 9`auKҌյh۩ #` P+ۂ0&i'm`_IVr9 Bζ0*pFBڠ8CFN)y/fjh3bqvq{<[qS_||n렏_\1Ù24 *8Ǒ Y =9y7CƯ^9&4S{XcSTʺ";fUv ؎3HC5ے[+ýmů>vqHUh.("]P-"2 k4Fsף _vF(Vbvfg5G<_9V?ՠ!{*1'o/:6PTma%K|59]WNN_dzq@z,(=;nQ J ݭh nKT@Ih,є-V۽~N>ZG GG$ۮ}@u^8;)B%/ŇNe]پW5{5 :U Y!JW{3n_nFgJ]߼/s;_la~ ˧UuWs*n94BfXa'2T 75%IМ|iIo&iĠpQLYTKaYqRk--|a 'Ǎw5 ZkywjFE'Ӽc1H gI;Ek'B죽4Kͱi4>_?1r#$FH(0!!0nR|,"W\q.ĸo'EybZųȳ>Q)ZXd$_+vr5VG81: #<.GpL82v!] E0"_xNb1򉫎^i4ibămxĢ`#&eYRb >їknCBxcHk{lxFE5(i|Gtwg%1j쬤CM4[dGv}M|m"<ƦAH/QO|OAF$6mJ2S6Hm0c6|ԇcK!>qx솏AL܁A|P! on(8ÞSƃ0BpP%ab'O8 P愠ّSpP<s@*DP\IyȹVT۝|cD I24Ӌt~c뚵σg( Ϫd0 #p#d9A$8j:8{o8[l9)q!nHn#fy:hVdbhݣX]CA뤠BhQ^`aE8[zco[nGSʬ ۊlG1_zTRuB5?L60ذyzʻg \Ph=2 2=LɌt=mijǭa쨾{ꈬÊ[qQovnm1]{N m!=mſ:ݳ~B b+ICzW&0#A NK{2aW2]X4iji:3uPG%'lL_g3GG&oB! Vy^#:MbPlȿ_K(3,NS̨mf 7,N~p%.-"^Szk^sq3?/m5=X<K)|CIe٧M\R105bfFiʂb >&#Gf0\NQto]i{yo# fHUsa}w}챫n\;HR] uAbC/ sguU!|:Q9Nⱈ󴛎SK;C6ˇtf3e$A"/}^T^w J핒1ev\TS冯嶧ᦫP#`'zfQ#[76\y` އ38|g4֯H6Y)\^4؞fg Ѷ6z;D&O."1}Yk$G }'-!3]%l1vla E.}!"%;J9BEƐ8NQ鑢RdEAOK8Nڗ,(kCzmR@]QV&O\Ӣ:;V9dtnC5z4ѩfEqfaFF9^qlQg0,BͷF0Yc8$t(َT3vx(a98WzMbYqƬa +$z"z(\)ւ0U C__+N8O3L [g-`z- b tYnj18"7Hj. L`Ee@2P #c2x'Z ܍ 3{P["#D29;ۀJ|g}c y7D4퉬ܴ @vU’ن;Nj\@F_w(L@ǴMoѾ0vw:"@W\K,.y9/+,~ Pxyl#\M;)HF/# JIX)9$u5bX&=q|ܣ6:cB "n8D(8 :srzxܰűQׇZoXu9& BHj@ ͣe_Ú6BINA U6>s-P*<}BoD d$iFmKL(fFy\e4An&ق-`0+X aђo Z(_8"6D(dUκHHpE)mb {Πߏ\/bh&J 4(@5 6 .6 *?;ƮDqmziZJ]<+ê+ !a-T*-4E~/SV $A|KѰNϔ!re,镈 [,(<:ɀE-rnNnd0k3ԛ1#fO+5_K~+TQ#W'tNJ6U3U|V}nPV~z-/lwӏA"_)4~?mjSWfJΎIGǍ߾)$QSUxϨ?g`v!vbgGwG3ll QMXdfnj١llY=BE#Dl!؂"ңX.-TTQ8?C7rVA&Z#ߦv;EfKޢ BY*g9`Ww8yKZd.3\ &l7IwScgsz։WSi<1^:(Nz4k҃H<2%FVLV`-P=/jqZ D X'$\h@q 7c:ÐH#€~o8O ( gCb|||օR(ANs.e~ۻ'CtI%xG#= ϫ- |IY{tɝ^1^-H=ORIh!ևQiOP,0ty@|xq`$Pʇx#qҋ-8nfQq_oEoa&&`aLp,:?^Z4A+5DÉ{QpWqv#"QʙoEj&vK2\C5:dFk5{عh^ҋcNr9qc$ײ<[rW pE߁8ANڟ̑#`l uDP %K{'VO:XuZ?jq//vˤ&ϳ]A½!BL?;-}cY~rHX=50*Ո xlq<2PG*革Lx *C  vB%Vx |_vF7\s͂XAV,VX8w\n2|k _2:{lm];V*e]òغenww"MKXsg7M8}n&x⾄3$ R.̗Yn"gFMN)P{r;SoT_|edf#3+i}cf0E՞@C>fi(Û/qL51bnI 9hX)h<^pnCǑATE7ɠ2 L\o SݻC!n?}W2 //ֽlSzbavdBl2O9*ٙo]_y9~V3KΝ#NM1n@>`V0e]+| PG 6Uҋb)N i;YŖ3le`.xa{ǵF B8< Ӌ#2T[17HfD|h{.dCAhH3$'wFj"J UOk$X .'#PN)VHrfwV7r.#) 0]wD+׌F Yxd{8=f쩓a$N!PϜ^9!9s)+Qp MA3fzkS֫,I' )k{x)-H514vB$k0Pqŭ `I8lP+W!@k /1uD=Sϊ D6w8ssxZ@8©o~*E(uъ>ѺxO 0Y#T;Vw s:(̇)wX@H%B[_Zg`tTX/nRtJ1_I;%L5ZZe ^'kqAZ%3wo4wqI`~|fߧq vf8r0&+pkA ē>Fp K3b U?|*/m(=yїop <ØKug }ss?KhhR>YԼ'؍5~/UIk>H^듽᩽NGc.ѷbLhh]Xf9Zfj#6d l[ 釅.|ՂZ>8T[ =r[@e/kt Ada>ΰv93u1|LۑPq:ؔ`dBV[ Z!:}T( c-@jZr6&RI4$~gMĔ5C-MhD8,QgPt@I8C$mil3#ג k*z1]GDk`bpa @jIh~Qx&&/@:ycfR_MT#!'rd4Z>B V ֬䓱֎U )>FDU@:!.-dI{ []}Ko !*=N,k7\l4E-C{1쵥%0AIFjQqG'jt8`Fz[CpolfHp6,<9#UT U[ z![+/-#G~ٷȿ%N.:;`*H\:GZwmOR8MDx 9H EIAV⸬Yخۤak ԡ}b&&Q&˕|K ?>Ov3"Rkx8o%3řU#b9w4s29 z*ZgDqW! 9ǔ[! vV,vDPW77?bkc;H=s۴k~+Mì`c6W_V.~=+LĤYZ-48.t| !gs~%8/wBҕ'`ä7f73hXe1R~7/ype#$6mtHpI6eh 1D2+A-\YƱ@YBR`&QR60uG B' jL 7H~IँC 9G xl )! fog ?ʭG_ᗈ)2boϛ#4oQ"ۀ:Q 7$0{}ǰbcA}@W@ozT8~@AUxW6^ע=U[u7D͙1ǣz J='61Y:єƏ옛rFL9`i8lk4wiWi^5m3-8OZryP6yPAJB#cV$z (S e;!K{Q'ۉޗ=;Sĕ5#[҅ȕ(b3y daF)yv4{-CXZ:S]l;_d[Pg(ap4BnP+|驩~đy?(+@3CmsiK~yk3u˟[4?D /txܡCv ٝ;wpdݧTBmWj*a2UGN Q~1*{=<`3,ċiR#6E6IPVB@E3i c`KW},ID:mzyE#a!-gb8='X;l#[| |xu,5DS!PXkWC Ud@M8\0Gt{' S#:Nۈ IH^Bk([0 ZyeڈGRFzWSj*.zrfzqs'<4 qwyRRKDEoT+[1KYN9e 4)3+)P|,Ri_4`O++}yBr(Xں +ob{1}W Si!CbT/NM@dӽ#z4OJyU·礂MsԨ*R ΂mz5m>B(NHcf lD.տ&v5|Xm] eY~.I6Ţ_^mi^Lr+Z'+p3UeJE?&BO#0:d>G9e j2@(R Auj"}iAk߉;h]a8 (sނwlS$ډIZxt|zFۆiA8}NR[_JjB0P)p(yqWX%#%t(lX7Z># T#$a4QPRQ5Wۈ`{Fɢ2+ޕt7'Afwp+_^8X b_JJ7d;όnrk9$k,ܣ)uGskW>HevD*~΄kyƺܕ'XXnLRfo֜@[ف#|/:χ>4hyQq}\`Q]@'1Yݕo}BwqH|88|zR%l=30*2&zQ)?Z/hUb˄eׄȇl/@18$e( 6 =vkȊ,P\, l~}l톻A {ҕn0rs@aJd%{M<&dzUQtg"C.aqyB =p0̀nnmhVت.ҍΓv:?iUX)uSc/T=I( MEvI(q=S2ޮ(FK/HA2Kc/ȊSC[aN1γrj岶k*G\Az? GuV2M.9xM8l䢪x* hG||~{Tm5>7z]K4zxGJoAى]dl<ƥڸH΍@oa"%wLOiOiꢘ F&ywͥQ',F߸2EaRPHKR" [֬gdk~a3L5^GxGxw\sGGf7 E,>< #sfS&gHy3|ϚY%ʓU<|d+9367yUmWLC8/EeWЫTVa~./ԴatmGFAtm|*ξ^U)ʼat94؊6akDϫ A$)=񛼇r,E 3e߂Ye({ոf{PWa\W9&,ʮ}!,lPm驄BsTz% ԡAk|yP?|ߝT q*P'҄$aBUm \QАq/3̸(nd^`Q %AD*#(M N i<^@fT T;A??x(?U4OJ0, k3يDcOR2aѠűr+I mT0.8'&?PIiB8lHR&(/u0ܪ61+zLVWFֹ{Kp `̉Pmt'mj/G'jrAb,l!H$AUGw`77887,L!I(l l$uүtJ_ۭ~o4_o/ZY_2 XYwaQ1yUتӸ {1>xj KY=*= Sw7-W>R gY![Ӌ!VڳvlH-0ůٳ"l5#6SWcR+f'U-5%; <@HBBbQrCGMć5¼u4[zQ}18+݄$E0=uk' _5}9w\,EW6{QۼDܣɂ? ˰u±;Le 3fcOQky_ I1 `2ce(`_$|X\?^=n)bG )ҏ}˞lةpg3 m톲Iq 4n]'Jl6 $j_8({GŐEܛۿ,ߥCzqtc=wH(Fcj|>a bivVf'~]RߒyN8CvUVøDn[fZULw$KXXi>I. THV=oOwk >#wx"@2k!>2'ՕE`.:/.#hL )8;T'9\8?1,ǀh:tj|C0V` d-WԦzp7#8'.J4l7<\R X@JKݼ[oՏ5[_Rdf>7%gnht[hoQC'S̤4#H.2Ҍ W"WKKV`)lʈIﳓVo+d?!Qgمl|8ַWmVUS$E?٨7O1S!agce5Yӵh=4eOua {:e6)=e?3!E{`H#H$Ȱ`ZEzO=hMo[iм4 A#$ Khb< ] ,!E%"Gyi;c`bpT0>JSvэ/G hEcNL !.^g X r?!{4t4Uoy^^iq V{+)1,n)c|砡G=!RCfȳ9gܕH[,CÌ3ʬ :"?e`C9=6n\^7T;d/>jA;mA\ d;-OѦ$PIos0uNu;gV&7)ZtKҳqJvrBd`p&e afMD;+w-6pknC.l3Ɣ逕|` $ u~ȥh56Ƴ vי7SQNU1IG–@V\,v>Nt:ݤ5J/HK[&,*^b0Uri&`˘9 ΃ÎbG:P[5tC=e 6Dr_+o3#]zp|GW0_HGljIş[Bº!) N8 Z'De|P-9vp__̟i W¯6`Y)[DQSJFTM~!hTl T{b/4z}J^|CrIKߛ,& ۫HLco'<=^cG6,QKWo@vio$Ґ5F2RM&KX}C?L"vJv0ѡzfQFB I%zexb(=2zZ*Ki2l0 ۷Ӫ`KI}{0hH?q#5g^&iWN/ 6r-*? [e!㞝8,)]m/bϗ<]A۰:攙`8FN T^Q;DEVlt_G Ku_ʖņŶE VMdWoK]tʑiuj C7*Cҽ )K&$B2z*M5F$ByDCY{ᑍK8KŠ /g?ZV`8*_n}Ñ+*Hh2JD*;(\ @RL3D Wthȸ8G'gcJ=ze ^J\Nҳ6!f6+ja?+p5JfQwpX]MAINCs8{ғ0JA$&^4!JIb<,Mlb[`<;l!\Z]:;ͷYD0FWjkz`tps_`N' Rt.!0ݰ ciKKwĄ"mJUQAn/nDpA?uayF0-GX̆$ysdHLrzr?c\rGmϯ,jDVoS|Bu͙V#X;yeW;PPEI "N }w]%FxKr\yD #_{J+CLcI7bER9& 6+uSJ@[ n "险 63zltǶ19J T#^ 1l>{O%&TX18W26K~iĒWӕ*bJ#:msfx>~Bo2ULD31 11s1UL܉ {+ 9xսtU7No)7'7$kb"5H6bAJ ʈ>;BP,`ETfss.c-=̙g޿dM?۴t#@٠i<~^LSo3A9l#'3ˑej&Ef9U/1 79]?΅gL=֞,0ʷ|͟Hڪvoi'u i zB,~%0uN|^$MNEvNi\RF b^άXp6Wgw?6X  bsP'Y^[0coKhQl{9l^IJfzpkE4v&1P?zƘԄ[́{]P]+t -J8X1\iwgzN"G(~-8*5OI _4?K?J_'v)ޡH[p ReOpc媮j\j]D(Ch2%Sv-@΂)E3f чXi&ocOyjllO6ĕl^9xڌ$I!zEoϺHhKߓ` 聱gVKBsL|.6I#s]RS|?: -YUZJs蚒Zb.PlJ89RߛG~ud3`xP܌t:5Y\I粘 !G&~[/Ӵ7$nVeQ&/GU4am@M"?p>־A]R>4pe5;ޝU`KOgSRƍI9M`1jWb<!؜X*tXFǕFs¶k?>&UWVCPIPEִ~ex<Ul? {sVM/`311sIEe8cVOE/l[!g@~w)Rwt~](bt[Q8)Rua֬^u@NPб E키= gwu{b2᧕|3K`pĠe!gB{l!hYrmv0 pgDY-Bu^\%/R Qj/"vA,wR!lͳRLUhAK8A|`Yu"TFHRH(Qg yiPLmw^=ukDb"Glex`I拼o{$U2RFķCm|sbcjSu pt=( ;e_xA,)i++PE^U3ƮcVLpr6N)P5P83DzKs%oas ڹp?Sr\)ő8!ȅ\31J,TٍEw$uOLFOL|ICŪQ0f'1r(SketaO˘dp_ v_ߠl>Xw<\4k}-$^Fy?0vژb <@ՋJxxE8$hd4%IUZ$N} $@t&d=dz=ߙoP[cb`enzcker@u8cJ+‡NƦ_ޓۻU/p lj~34V}w:Ϫh;9B[WzQ\z1 p@o>b۪zѩbU 9r~ 7kReqy "ay㺺wK)}s| ^+2`ᩀb)x9,a`[ )s~?8s"=#Je%leeUr87a~SIt0 f5NtB!*Uj>!e%,,WIɄ@(|KJ0f!(#,}yIJB^8 D2%&6wTFƏPx<ΌOh ]<}=?י3@?3MvaIN.L ³.L;HhҕihX 7 O[4`tnhծLlp 0@̆X<-GtsmP_@&1 Y_dhr73= ,ݪ%* K-"j$ǴC\G /CέW1C4d;JR1 yp${ᷙZ[ d"a~ɝF2nJMzj)f'IL,d02I"&vtL4l-b¬X8YLT/Tt/z[hG x"ah#daP!U/;Kc;o:1O2k<NY|j͟tg-{ܪ >E)JfCSɌB^_ب78aVX*';LrNZ>әhSg.l7LOl {fŷCL2Z sHɞD#xv B὇+=g}N.wVK0`{bx6 {~Pds!&7Չ,`Wt`1SF92Ʉ2Cn!e8Q b,>ʬUXb2)zϸhj,q~L*6Mgėty^y/y_!?8(aF-6.=/ԠcO%ue&<`OÁ-g:z3sipYL XW$d%%Vk25252}׹eZgdn_gdY,"֛bzff0hOA``No08Zmd,6o1"+}61'2 z{Pv?f츉9V0k~Y/)h-WW>d)A0'K]?ü)1i毟1o-{&hy&YjdI' pn2F*4!OF-v|tkhLQC\+_=ϗG#3Ϲ@-0l˾l߱@1<9v(l ϒ lrB< XO]O5NUy!C]dP\hS+4Vgx/S' Yei= y.٤$N.9}G]4,$mCg C5b=ުĕ%> *)޺OT(|m 5Gy)=u mPX60bvj∘D" n 1>wjOvYSUS>IݝaŃ&X6dD6;ǧO,ud:=Y-\gvW<'teOtcpؼT-:p9~&H\6m<Ґs#1av/{D.tiBnl 7*fa#l;YȽ*@u8Ƌ$T2}lv4D7C Y5Zjy_sԎfLnʳd[WɊvW:汲|hTw[c%F,`} vPvH(C`cǗNx=1%SUP|?Du-ܕ{˄]lE\Ȉbls۵>`z鶊7\ㅇ륚vz,>sW೮z'pP;Y4 w\sH xr(%` tU|"#urZuʧ)mG) `981=оC7bLԖ .5F!7^ GK<>z~qnY\L1՜ 5&+ij*bW1֨u?=i o*e[{Pg5T1 o;cךO }lWxm!ol s]R]s( c5n2`U@clh16VUT` ucVxCl"I L?~:QXM2uM$'؋y˹RN#5ng[R0qPYwРzx+XodžNbd}[`_o Þuro?ayztU/Kv8Z+Iyah'pI(-جa%'ckpDHWJun!4s wu ]뻯zm8-Tm˭p5Y]/X\5 !0 =6F?؉7NHqW\*nژ cҲMi{h&#ڟFw m4xnB.ՇAM@8s|;8 ᮻD] Nb]?c o |1[>琗,(TNd,n:bT-݃g7`:mBmfpN[ i.䬌Ut NsWa߆K*"m cZ>TνG|S[G4y6p[ 17oimB$Dm4X{ۃ웸冐V$p>. FQ'CNq cWlW-yr<ϽQ}\7]%y7k̶ =YdmB;2K>|4^m>ƍStqЬ~˅VQCyX+kΪxv+AP>?TliwܚiM:9ǽor}YP~ޔ_6`o{p[/?2 Pd`6 ƍ7B6wŠB?e.6 0ީm. BbAv^S2}{͠/*^<^u|8b}s766Fp52 &z_ CB(}-|Zeio:&3P;؜Q{0o/nƭeMGDδ/mM;8ڿi*2Bd ;x+P _B 7["f-}7`(ԧ! kr{x$v5YȂ'Ľ&9<[?-K@`.f2 1}M/AFV 8>+XweA1ʇN|xL^ +ibV4Ō{kc9,ռ yɌɼQL69,7\pDXu0!Z24_uGjN,p$rSJF# лe{=, V5翫'ۙTb73$Vaa:#LsY*_Æ$`{23B"Rۃ}+cMs NBjP|U XFjJ`r 1zy XR2L\o pg3'iZjA Q`7s/4lr ;Zd(]͊p\asx\E; ap Ƒ4#^2O ^ ]H)7ӈί4\'+-Ȟ Usi.|?JJ0A!½sZ۬áA-?8jIjqmmbE,.$Vnsk݇7/]̗ᤒןᏹ܉bϟX_295ˇqV5B!&ΛIhN*IP$^/ߓڵǢgU~7R0VX<ͤHh?MY?XJ{KENS玻ُYѿAyk-p7 Z~zCE~ b/a?y719|0bH̿-:YW+eݖx>;6/p] K .ܔm("%` KJ?V1H[B)kM?~H~ᇹ{?Z9-F s:Gk`K~!GY{^H[~J(T`WU c fۇ:<B gB]8 \TπcJ.C d[)8'.MCKL",&øhMu ?'@i!׸ ػ&#f}? LNCtBABz^ [BE˷G#~uYDߏ To/Vj':8R`)ɀ; L? /흈ljHhbUIf D5z&ԑaXbR-5i!lvSD(|貚h#'WM(M"s$٤} "Z]jڧ۱[`dBQ79UW &0A2H. ՈP/\; ;!(ZAle$E}.)f0f7 >EFf4b%#|!X꼰i5rpRƭ\RoP|EA(+~E'ʖ /cV@JnUw AO?$kn, 701U`momB c3 M}'z( 5ZxkM“pd )['Djn[66m*{Ss3bћۄ/|D# )*|W*Old)5>Ƀke/eYiݰ`@b\`bT `Kc| K4JWq9x\KFdAݿ$c_ (db\IeI#uvrҤT4d@XZ?EI(\$K~?|~,d1V9o?L~Q&-MVÑNOݛ4𓠳&ڋia8뾵bWbJpFy6՞Lu_uM).06%PziTwQ8ZXjoƎH '*eC9Omj o^6le 4L`md#--8Uoz`^LgPMlP0xL?1^h^Va^EW㭀YޔHK[ gN PI\gM1֠}1ڞ n0X3Kbŋ-L+ 2>"&0z݋>{ 3l bLFyWDT^*~,;(ؼTn1)Xh_ЇDƀ& &J?R\7D*y]ۨst︦hcK>a|[cķt661(^F$1Yɞ"ƈ~[HPX7~S^{Bm9oFކAZNnAo%EQtwNwب¼17$;#f"HM{H{-' Q:93"dJ!b :0SLSdӃeg OgEgqs=uPȪ3D\'Ǹ0|p @NFuST}RN]/FPuAbh9Qk\;"KϪX'Ye%"te~زmdF<'GԞӡBL/.!)O)ďM`ᏥZğ<!&2;~|g4TٖlJ;Uw8K^e9^pqBO =3"9tOF,T`mԤq+Qƾ?jxEˈBN-Qq ŬSձ{8y8/Dq'](RF M,y%<.Om<= -9!fBS"=ՊO[ Mϛ{ɓhwyǪ򜹘J3bm8 _,3x` |ClKn)p$H.9 EKYbaID-$4KMhn.b8*~#hGwG|$dE y_K}(6)^uQ|Z)@?jXӪH\F2TȆCF:Ia^a\\.eƈ\^uwDԗ^'o`29fR'2QCEƹ:(D=f\0\Ǽ@F.XY>Pd_p$^PAO:re(2c e")nWPqɏh5)7NV5FȷxK)R W  :- q]׀p7 &5^C%;*Z uYXV=]bZnR"vqq%^synH#_a]]mrO5;Iob@Ctrݘ ,a}ʲ|ѵқLGNBK|DS(!k\cB*R-OG{zJmfw}TEgp2?Η1n*Y=}:HEҏ w.J4h@!kxXD" rDCF˥T\(EŠKO>. 7*@ZJM6^[&C0VI Ecߟ2ˣD>d܇1Grt9AYAk2| :hg$ir(,JS4r,l./Tˆ Hm"˯‰VzD] h0XsJxްxPWC|gP8V$dȳ]䦖#E\ўfdoJL9EMmL5K^SE+|)tlQ;C5oe0"o'to ] OC G_ QW_%[ȉt- Weܦa}+0cMc'8; <{J w1Zob~d?n/ؼ 1W9lxwۼ fAyq魸ӍI^´βClx)6ԒNW [fVQ!냘^W9snLYGiL4<4\7cޯPnnG0R=rG~ѽ)J_yN{|zޫ/4B3ݙr;5ny!wqxhۍmuhO83Ro M%[TTW 2}\BM ^k~w43^nN[<^Noid*pHˆscDb.Z.>75[$\X7 ۃ7˯07w'unIDvϖG.黫Q"h 1Ii~&4}]a r)c2i4bmՇWr, Hѧ\oe>xH?UeFqQQoT(mZ56})0ߐyIJ2G>Je)]mVm:MF6H)Qrwz͂岄fZ`5U-+g,R EC]@9^n˸ʹe{N^[_$qCz<1.bG¶j "뷒:Ɂ`UtW[R5ȨG){t;3,7FYiUv*ubĬ7URVp2AI'($AJY&*{[>1S0B5ȕJ,\NTl`[g-5UR_\˧;? V}p$+Wá=\Byڤ-26QXQ$@+8Iݩ]]<z`)YXlߎO5_'Z-қpQOzb:WyB*\8 emJG%9W0VZ|0heփ mzPijR|-yHiUL*n>کLCJ[=Lv(&AX vK D{J(oFEڹ_}:{uSUvTw~("{\ިhۭ8R瀥+(m28\r1SD<7ʔTmAV2es^IT8v2V(ASN?sƅfrSf 6Re*O¿*tu6oSzE$HòIeSJh1UaN󜲗\>MgUʼnLx=5T,#U<ÚK{ 3bG1nDB*ݣP\W7NR|A.8p@W'#IlWo{\8 2Zy8T{3"|աa&3I+}F>WmT}mlEnR1nt{Keu@%XKl;,-eV}$毕mbø(FB8=o"!7E6)$~oC0o`FO5FW5qKy w+ʲF;E(͑Ӈl-fxв&'-cR5U#cx^n0-)(O1:RJ(yQ7 `Zo4#F-!)IT%llH/ݺx,1jpm@&*?* KTͶ+xzěpt ٲաo|~u1n ZwB\2ڃ;=MH.鹹jYNϬP9u.8!o^wߩMaXRI(o-UmJ}cwn. Lhc*U(VC ~~OfqbI9+w>ݥ^3TORyҌ{ssu+W=F1|"%_Hwd55X2K,TqER5QLc/:~#Vmq5f lVՙ?"uiw IPXVxZ`9Ə5 FQ܃* XYu$O4Kr[mCa[ U7 ̹*PZo-菫j0fo 0=Sj {VuoF#f]cM OäiEciN'~&A3j]S\3jNEzZj{ZzcU/NUz/wY^תl$|EOPCQ8)ér3'nҏT=3,דtt.DDU|P"WlcaB>ߪS͌;ϨvjQ\)^ƶOSxgU>2GXƊ95bôzc5gM̃bcf7*PM A@t2Tl풋GE?e;ɚq)6E A&k!.gnCs$ SDU׼"֯!iB҉!|0%oe3n?BfKw5N*wo |](YcamB\9k$'Z}y܉$W](vփukf}q<L>wlRB=C;Wp~K4[ ,m(cgir5EKԾSIc :Yr{GǴ%-P{|"nԶ* Fy<,X{j]6#`2xrJwGDy?0h̘v vWZWiA.(BU]4 [MJ^V7LO<6`Q=ʴOMajڀZ˫Mϐ[G!]{0xڭ4C•Al6/eO-@~EI*ĔØTe.FzwhD%!rof/zu@^4fBzmjޠF Z ZSn k nMؠ7i\Lae29nbU77xĽ |[W8{'I^E%;k;v'q֦Y*˖+%Y4M唶to7a m mR3@d`)atd!{$/zᄏ{g/g[XgCu 8:1+?2%$R'B ?Jc@p.#c;ҿp%/Am൘ #\5֒o j8:u_|ʓ/ qmkcTl)n3J2akhzD=R#g?AK(8DwIR{\&8=$F'Q :S A7B|Ihm}~ Ip: E¡7QBbt4=H?"6J3=SΧϚ3 BgU3!t0,v ЋCAطi[φ;E '[ctߺ>ԳϳfM8ggu߀ ʤ׬)ۇyGHa7?.yQv;j!)CC zB19}WCW9:E1P'6BbL& Fhq[Gr1y‡u œz( "Ѱ4DctT8"k RhW&+*!r MM<((Z`M,9)&GA@wYQ m:i :֔9ehFBs( |K|iTq"ޘ;1:Ck\-9rЃ'4|Gxk-=#F(^f:῕)@;1C 6VY0b)0RC-p]LpuKɓi(ϕrK,|"S*AcGiؘ?$g~+Q_CJUp6-űHhR8$ɍBc?76.vNQ QZYc+nAd>(AoTLHpe5 o*Q ^OSt80"EҒzS+X{*2Ks-80γ}kߖڙri&=PU }D:WUaX/ a;Pm⢂7_@Cq%*Zn_[SgPV]Y,}.UU&\(ʟF)];;SUo.D)vS_kFTƉ^%m$wC^+c NY\ R=ѳTp E<^_}4Cٜ۶l#Y!xV9Q]$Q@(F*6GN1ɔDpzGK+тMޞmq`o@mbqG д Wz=ލG<GSrsy"[IU&lc؄_ YD('a#^*?h0<gɴ[$тFKi@Ak;zLrG VFuq FE$tı‡ jM:m8-Ksi*Fz!ysⷘ4FA7G8JRyl~MuNMㇱ0R|A4:B4Bs#*'oBʜ؈$?{:(&BX&|t4[I}kưg)HRE8Q:P-E\ %j)8 Q'W@7O?&aP/MG! gBd~z!oFE)\@fQ9_fHG`@-|{ +pJ;Qb̨(AlAu;I8Č){BNR9>ƝDXbDI"Z"(ĘT Hh:NG\^PlҲSrI~tiuL"N Br&PQ!cۈS+Xg2y[ Br;軶Q('R;Pգn|A>2J^mmr=ظ_yВuƝ13睚G՜^Db\o 1YC":rs>#X,4#KFp^q:܊FO1qrz"hƩqynIM#>(L"%Lr' O5({(g%Y%~ưqX~ "2U#e0"F!x?68z9-4 f'QN_=DM"ɻ.p\e Ąv@b>Z@7Et R-N*X~q;ʅ#.&k[I00&&p~YXVjG4/Rcz9BXEjD܅DՃ *,qe\$ )s- G[j8z6pēB1 }BR@\v]L|cũs(}fcJ;RN &dT8oAh7J}fJ_t{"p0oyG< 5k'c Է[py*Ojf8vX+5\PRL"[4w [Ym'/)[ 0&x^)YAUhI]l>iݥY~GqT J ̣B~tnvwCQ2iz$!utqSO^+wZCIxN72 &\x+x+Զh,'uX\wCUm+/(Ļ9(gvwBan}tff?{To@ك86 _ɦwhx: q]tz{6 h ʥ{K-}[ק-8-w_!v4=rQI=mFm)}\|p[N#s6{6oVװ%}'1&I0S=h*OTKB[ܗ(L Y-;SPF4Ohgh$FęN2UoϘwxtVY ,'E`|yЪE$4&Ǔ;f|AfQk!QXD&2zmڷٿ鶞5m<ӱ6Z?}HrXE\a OėWUE^C 2֣t GF)DDzvC WZ/ZV/T,%pQƻV[+CZϕPv[S/ &r<{m&bև»~ěSr{E |mTJN&'w$1tqS.8,D$&6!' +_}lM W>i(Om1ӆ$q3ĭY OmpK]cˑ{/_. \B X!3[ `L$KFY l{*֎@p,S^Ɲuu1g݄.i#{cNs"v"w P<ЂRƕ0a8S5Eb3DVYp?cź:H x$:M!4q /†gÐԱJȗ& 9}Bx3x;\sXv/rrvi&l{ :J M#3^_*kN0ص=[䶓ll!Dndh pw3Si"ٗ@T?DV>Qp0%HDEs&#J [Ynin]~5^ uhWN᤭HNPIn)]䦮_;\ҩvD t;I+⵭ͣؓ1q. =wH9 8H yY NIgr!N?dš` ]s֒UzGe7=urI=d)@QTUfV"(kz։w#Fme˴J>b6x ?I' U/2-}=D utW6s&[p\eMF4] ySZ[b7,.N\ SWC=D3nJ|u%C# ͅfU,'--$.T %A2?Pp膞_ZJda$ZZIDAJr82x t(#-i uj(̓-0+6ҕ:RߋLCWêzTy0#rEpKgC*%@ٍ絢9 \AM>oK|jQqkhEC8,r"ap/bQ'b8m҅cV.ƿd=Y:$1(:$Ld]qlQ>!Zƞ~A ;8+™l.5b~j^'_5+fJ(yS/$m%&R:RI5npe/7PNQ1 nۄשx05ZDס.DZUrz&{`=).BTE$QcJ#LHPG ȂkQq)J7:{!-OhxCT UŌP9s:"/yc zm$>S ,M$؆ SҔ6OF)9ܘ wYM{쪅Y ?d$rOz9k0!Ď 8{6[Aj>\`{(qD`ҐqȦ.Qw .%WEﵸoh<Hr!^ ¦t>vQ2Zr6Jo<\m R]t7L\{WU)#J.$A pEG< 3 US6 =ƔI1Ex+~LgL(O$O-tkHç2iqFj?hYy3p+3Y>=.MQXУ05uWjQX7OD/OLkOA M*6nW!c6.(ry1X+@oWZ`·/=m XUج|sqS2`,K1Sdm h^ [nQJ,\z 4XԨ҅ն= d@IK%$Kw7!~PӮS}nLAjފfHU)eJmju=hpWX`~F0^5ԝ`Qa1Ĭ|(DzH 8l[o+|˦|^G#wr-9pU\MgҏOj*Ф4 ]W+{+JwLSSMyٜ q.d3{ƛ.ёؒQK4;yu]ٺ] fCt'kh&8YCW֊8(O(e5\]Ɵ"n&u5;K|dI#z%܋OfFT.MDCFc4r:wtnC:X֡]%:&k@r3SbN-dکiA¤f0aaSbn}PoR'Խs/Uy5cMjPs6"FI83?>B7Q pSHu0{mNV 1oLTԒ0ĩI9Cέlc%,(>:]t#@=,Hc*ѧ5G396?9 0&CzuK ɩNhI]K]{!@nkl%Emp-7Kȶ t(wҿ #TɊs3lhpO/؄|8ͮ-k<[6m mZm'> ap̏yٝlcKn lܥE=-ţ_t=č}koJ qg˜h5Fڟ-4I]\WvrDܫ>Fqv4p쌱@z3[r70qX=G "&QQM(B :l6[̀w| ,RnV8C=ofK϶wPIXe&щ,ҩ啅+l} }cM(e,{ ^(-4%iv 6\!g?J~vDy8c1᳥J1܌wI0B ],W2xIJd+ |!}·G łq/ߠ]Awj֝,䱥%PGeYz$lذ xՆ"^eKϼLLLLdz+vHVJ2J|<~MJ:PN6 5DJ=[.kJU`ˤF `EC!8^W%"x/3 ҕϬ.^ٝ#=7OKTۍb%{WrB9^Yn2@7c2rfYNJU֚SQY\ܚADw d`^녙1&}KpNȩ@׎bB1# \[wzꭐc1U10l0-M Fں~lW9OziO! Yy8^e' .SIɎuOfA&V_4Fo2$V;ZՌ>zk)>q~;j@`7 ~?)mlýg%Zr&M4`ԏ9 ޏxWx1i.k(GOU,KI  n4W ?Qj!c5/>qg4yY&|?B2FS%٦v*]Kp+u}wb,SK\Hs24gMػh ^Zx)#|ޑ`}b4PR:: GtPdB0u%xn'(J,c=&%eJ_zW(pۼ ΍˕w_< ,dwH{ pْhyŐ)}xAbK'v4 Kjf! \?=SQ涛lW=68T {Ѱ3BnzՓ|}~PXKc"91TL*~7ߦ&;g"Ԕk4JO=N6]+#憸]}q| q i!TͽكX{H^N9s5B䖲`͒bjYvFRԺ'a'JU3 R7Ґ<đIN$N?L"da.:I9s4Hΐ.2Qs!l Hg6 ["Kj{ S&$ҤCEF}˖K mmB֡Hȇx ka3ơ֭[Ł(9N:4@) (Pن攧(y!G+9͙\u^LD$#g?mä_s'ڥ;d_hykN8.'%'7חF'1DƧiܾrJb!n U׍pfe?F$g |QXӿuHS96h0ۂE`KwqjiĞ3\R do刦)iQ^cAUA6+ު׷+G`C8`y, yK8nd-UdLNn)U#H8F[(?HM-Jӥ 5 ndVH[KB=<;TMSbĚ*M!(=Fy烟ȘZ< drs: YZR'6ЂA*!(*FwҠ$%>g`2oJ&:)Y `,+!`@>ھ5"ґGs :64DšQ dGqeK1d5'tE@@nV֯=ΐΏĢF鐦] Kmd$a!+.iBR&vR%~Zgqu.rhWd$.fVҶ>f5`?tpޒżT3S*jHM8%Sio" zRð.ǁj%&-:Х׊ Fv]e9[m0˪oEۍ̇+ׅK;|˼[D룜(1"WhCrP@䵥@ JZ[So&\h(~Q\zE뢩cg7qr:v N,fiLw.6pe*ƒp2^hDE$\f~&n^a$Jn\=]}F>]h~yNjɃUxhʝgw $qH1*+6O̘*dHv1,BW님*3m#.6mcP6Rd3?`+acF*t0{A4F Z̬r"%Ȯ|DYěY3UF\Rj…[`[{ ?A3K zcW,5SM]ǁnю YU":xKO ؃v*T *8%$$ ]+Zф$ AK~\)r'SR(j(YZj/x1U4)iZpD~kQhO/qù-fgg˙sc:7ԀwL#R W W30x1B +)`2 CwW3wn_5Ed5M g ,g6Ϧui4xB.RNR:_g#GCZ 153ǭpj c0ӌZxai֙a~!w9?sl$_pO~PE!@0sF\8 .2">Ɨu^"t׉H)&}k$Y&|[<.()| c z5?bأˇru r? db'σ+FL̮f̄ s+1f>ߚ8Az)8? 2H8UM YFH|P6%/I$.*BJZC ,x1kdYS3@gHo̓ǭ]kBr Q<{[z<{6n[ߴy{;[:T'l|@l`)1rBY(j7upaY$'gdj |ْܶfuI 甙 qjSSJOx+ʭ*mѤ)88bTs!8e6s=)3?TDS<v&'v'N^}OwÝ0է%A&RE|eNU^'rFX7T-yدS}r Y|6n*͏Ʋh ̘r  οNb .,xjs>;gfdi 'v!Da?P_}=6vFZFPBMwăr"r Z<Xz@ǽF0ErotՆwYuJF9c>u=aVҋj[u)cԕv1P~$RX'rU\kmmiN==ck\e@OBA( u(rPA SRNy| ۙE)ڒDuX!Ch-p̊WB!W;6Qhډ->dii+ 億D q /'%|e?S7x9 koE>:' pqS,G[Hj_d҄U'e!1xPSj Hxc|JJt|Z5q9 g{NwM _jhB2(i'5" Hg|4ZN'm-9okLm;]* /J0iTI/"&s y!ɖ x#M-9Dz$LxMC:czt+2m NZ{ÆXD;=hxB/HQ.0 bf+*F[GV :E 0>-ۊTT]|dQNmv _ <[&(maо7tPF"^nkV? DLo (H iFA3U#/&$WyӍ3Ba>0/f5?x_3W$[L#B?lɬ"1~IsU+LY]{d.>} V%_2>W΀dBPKt! _b a9kdBwI&\[[^TF`sY*vφ5n{Bl9z|^-꜏8R]G1&oK/l4۠Rqq}k\J8oV\3?kp(k<8eZ "kWn|aUadx,y-ePxLp)~"p\o&J:BRi6[Ըx-WLP"O v60uH[~,|rűvYAU3YCbp)NlxWJi?g*:8iro|A] /7+dFj22s>8=NgvFclPM%c i&PֈO+'g^]jg*jsߴTf\!_'V0\֐v+[ Aƍ@L)1˹v=XtqCQ!b#Y+1:I?N`Ĩ,JJt.G e0s7&~T *5nc||4ۙqj$oܯ%y!ߕY%WxVk1ac1ɪNGL/B=~ .HVd9B;^$ы aBWTB+ 6o %g`5fJoZ) w#U~ 0A߸-SIH!`]YHg %v^h x!L)o0iD΂"-^Z|SY VH NvTNZ2.%OHűJog]ln^|> s?b8`l;7#mCbAjPA(b+RM .M`>c=E|A4׉fj[hoPX.Wϖۅ,pdU[[nCRJ:Im,}d3k 2~n3!M=׽I_|䝬.bmn0f ۤ9o1&6ա$|)\+ rgDR(WNq*LGI"lpRr^y#$RO -fKd}3-P3 L(.! Nq-)a19\ZSWqE&Fa2u.)؉낟GE3Wiԍ{~|ǣ~uʳ}![~<`]S֪B(W<| w?Dk\.׸ì`PsB|uC#G5c63B[ p_L\l,g9}-6MDK; *褝v/\^޼aVMC6t.#FRp[-ޖ0@!nZۿeǻ M탹/Hy[r \#W yR&*;;&v'u<s=iJCTFncx>ln7 iX36@ bnhc~{z|OaKo^mh8_=}L9MFm:I4ۀJ7[ipu dǛqjgнk*ՌihB4#hs#Wjҥg8NBÈ}QI'Ux5QU*Ј)IZNbGY$괴(qұWVYT,>"&iU K+L]16ź:n6/1\{)(##='J$JF,&M qG<;9UZxO–K+h7 D& \MdzNpS[}--,h} yCsIY% j92Ex1NOnd{Aq>璜o7ìz̀ϰ-8?ފ A66 WqxlN6ϳٖrYSVcA}K[Tܱ2U<= aQ6,*T+>5UZ$]Yg^m"[mzJ/I>N[ 8Xh#7i$`= rEȧK({4/t*쒁6hpɄ+^xV[;:=^WeFZԠ޹j<O=̋P%"Z|ܜYG!߮ ~*dJs9M.!h n3O8S JN1lKc\XXrC~ׁJl~Z: CUĐϧ8񮄃hک\!͒\l ŤIYf+9Fӹo#bσ?]^Z+G D%GKBﰅYd+u(x}\[*Db2UΟnoK>P!C xk{TTgg,Wq^}@-B wt U,eOM֯YD]86jGCCSK*2RNwSmv|gĔF@OAOL(_ĿBE,O %>lk!*~҅_flOvA cؤdT/g K*,6CEFx Hd +l :/T&x'Z|$R>r)Ñ,|ϰ^o/LOE3ב09a(|H93֚8R&)tcM6lj}Al{'%FQ~N[uS b/D'x̢>gl!޹]Dl ϱ%gPFr1 9acYŘ81U,HhvD ڲE|gv:R Y`mʉOr{?t92vuL/kJ/OHH-OyB/ĸ)C< z)|˶'\7y x,p⤠ٺoV9@oY;#HguWb^`, xc3[U\߁I,[ ][WjDh32,SfWa*[E]U!ހl%K[N+ncKLfo;!Q yh.2x|;x+Ml+3)q)2t=s>TeDZ7?<~PyV<+jj'BzW7t| Iq_Â&Y4<ߺN2g=8?+r=363[-ȓ.THr;p::MB 7iKt"\ө:٨&m|Rٚ\CBTʊ.C3-$${F /:nģ\e8s?2ڸ&#C&< b**EK!wy W_x2VkϸWKsB5 pdSdt~C*mQNH#=ɓn!rD;*gw-nMȷb'5N/Z5koc;Y[M,r%\&H&$*uv6B¼7'pwTYJ4xңc7ɘum ?ٱەcln1plA=V5u,ݙP]&N4>UEi 9s莾c2Q/2E5.>Ad`|yfv.CUAto1Uwoe3 (a0{܊ M X$?ʭ+pmGhu-oA\o5|;'ed?Sfd}j)w 5E"SZ *I\UTTUi?JF"ɺ0b6/;I6*7+5}nR %~ kSi4FF͖⥉/x$/p$@CB> mU,{~N6s%8 9|fa] 9%L>p J9s\6qJe ܇QF&+xhl˨<r4BSf[`jj\s_YF6 UB" O.¨Xaٸ8[iZ&x[J}5pÌѪ`ʇҩ/gAm x+U;!V{w |xomj4şr8B dXfz9>YUAR ɴ/u%/+l)pJNeV\ 2t,/s"UN/ .>!wr%y4XWfS\7k jFw=I/WvpFMJl,r4_KaN*?~i<杞@^αd: bJ(W$YT@ޓҶX,:"R]ȅW\;xw2`}/Q#qɂcAw^lr/E?pKGZr7rd$t}PRLND]䅋Qn|h#Q>Q$h%IF.JU^ʵ%l]ړEG{7zj+pE ]sZCxKD]$ d٦ U0UK 7 [\ }\;~\)9d㨟*y;ŋ$=>Y}ma\ !rzdS~]q=[)~쓦r#G!M0A8W=L9&sqn ||ʐ CqŚq’'^W8 z[FS!w'mǕ$LKGP*|d˃q/8}ea? ZvsqW >$hnۃOrK6E%8=`Eqm`;-2@(7zV/FFO xUe.0WJޮ^Bk&}.N!6kjFOv$Ni56GΞ6Q[="WMrW ٙЂu<#\ ʩ~e5 OtY E r\rpSS2fq1\•(eQ?r"7YG dbwU9lI/2֯ƸA,4'O~˒_Ms+zbD9/< Hr#? H3PgR(/:xW!WYV LU /d/p[+As5A2h 52)^ⰱ v96ZELAê\.>ykɗ V @Z^ VղE=G]\:mF?xp i•<Wp7(W_^x#t?},G =Q<ϕKq3p'ݼCR@lsUKk/s㨊gօ> Ww]Iot?ŕ+|1n3i]9ǝi?">u>{?wߔ 2%>-U޺CO݀Z {??y$.2">_k 74:rAȒu{ㇸ [af9sTZ_7"Taw?B~6?•ZU+ө6xYWo0â#l5Y(40Jx `2Pf+G^7F({q$q.7'*ī&R;rjP@8oNE/u~Ƚ 6 XnDIzYeE@#- IR#JL a{) x~3ε_ɏ_:V0н)$\שFsbk-DϿCzz*7unQBͤN,4 lwZm8Ļ1 ?FV3֒7G=mԉ>T?{ !C;5l`qOM{pܬqM"x/2*P>ށ19A,n5RG]`7+F4~sEe?rsѹ3s޸ޓ&u$LP\70{/Û+!͖ZA<Fn196; \2d;+ 2oѬuiY6WwxvŐ k,VXnkal.Uw,pIʐYƟ|{S\ ʘj-g|ў0HMto8L#P3V4qO0+:04cȳlQ}K9p.c</ ՕҩZK [%i}sFd@\cnS9*4-I]ڤ3bʗTvGyXj[^Ιq=N+c >$g˖;<xonq)\ ok_1OM's['Wf[\`t DSh;Vnټ펡>\CF8:K`H*w0d'>gE8[r]=o{oUuf7{&v3ٓI2@ $, P$ Z\*.VZmbRvqkZ׺Kmwι;L}Q=ܳs%Sp%K?L" Cg.߾q%sږ:}?YS'x$O f')9t#t%-%Jnz ]DZt<_"F~4a z'eN Gy$(wSp]fg,4;:lh Jٶl aKT 톳?fݑh2 l$5Z~E5MM@>&D39Mg$(2Iv${)=cbX6RˆsFjQd-I`Uh60@@ X߲1pF/8! VKe]j#,QUkǕ"1|1fwpXӱKS O=6:(4?R s$cqn 9bI@)XHexi;01F(6EI <[ 8h''2)D)I;4>kWasw?R1c6X"R+"HITRzӵasw4>#IGRDE3<+11xS\nK)7k-J$T)&t%| o"s /X0͗%-)c m9iSsk/6e'ݓoɋoL%ϭZLJt5&\q% 2ӾF,#mߝJSp y&G[PZR}UC}*#o޽Jd=lzT9'v]RiI̅dr~X@ߦI/' nPpOM*%&Ws+q=(+EK{i-:P te # YOe]'t V^>H8*3W܅Rq(W˓CtM/LK!SwtwWL篳c= ~Zo"U .Dx"@Qq%*"ߠjEwH8k6zFJcFcl/T~1w25ДƱC?ӂ6kBF5+j 9Nբ QC:ԯ@3K= ICzpLR[ڳywɍI2Pd#.{)n#`K(.CU+-Oet4&.$ !+AǏ:N :}1B1 hNL#.}Eն ؽ=0Yt!Ip!!k# 7?:>3tкwSbF}}_=n}"&e0~xW¡"x<gb/²Bղ֐/ٌvW?JeȮ-*w2l/嶸G&*m -|k29cA҄2ZqeDzk h6aG(0CXWF`{iV,/]Z#ñ!bF:W: //IP>@i?#bh7zY@cιBH2M#V&hSɛ=ָG dp} SS14Ar]Ϗ K:Ze90H׌*dCU&+Y7{DQ18?!ҚO WmpthP؉ݪc#噸-?JC b~hMKT FIPl[|U]BGRz]2 ̶@s1 SbH(bg?-?L0e1܅Zf)P7ze=U8}==JH.ak&rw+ѻVҍ3UL <95+`OiDEoBTnuC8I|R2 æK-PN tQD&Uʹт1R%\ᘥ2W]Du$R2 cLg|3Y' "uEhQKP" ǖQ>u]b]n,MDfGgx@J&qގ"}yjd3aR>k~HW7Ғ+}(]_yG"G㞡=о{?dH6?Et?MqnD_ʶ4_6Y{d " žY%@|<ܓ gai8e=gv̜=]aGv}k@}/+sO%sP76⽛Ԏ''#NwH3? 5j pRU E.nR!4 dFukQSZ&_PKj @ljZ8${K׀pMY5ަH[o?gΧ@+{81;_leN|a|op'gnf%4z30L>? ~vV???[[)(|t2 5f3A.*N%dQ*B6j!ȒX*Gq/!b07R$Cv(']*ȌL%}*: 7-Q8p<=!r;B A3Ga%w)Er6 ?faFed h$HR,18ơV©b'ѥ3XI\J:Q1ЦUhԵ45d@25s"zT8x áM[(jHRp͝T9Yf-^?j('FFcXxP(# mM-=e֙ǖS6n{6{ƦC[gO&l$fI֤(||")jk81^[ƶ%pk6n eA/'90AO쇎"J \DT>8.d3 Cl {5m&e-4x@}'+wS؄%_ ׆^ ~$ux>PE +<΍ pP$QjtnCK\LY* кmW1)j/m~M 8x%kڳ52;R.'-mDjkmmL闃f=CՁ6@miRrHϼ؃Xb)/_]tXsg6 ޵ӵWkRK)rpq<'B ->|^h"AR) Rp4Zbrмq2cqzW{=COE?qH=&z\??_˘h)%o`2`a Ɖ`¾L#Xǔ:&cF?cudXÇ #qs}0ji)VK4&;ֹgmi"S;>m8 1ߊE+N'R56ZM6ˋx=,ѷ跾|n@qd%gy <70\ƕ,!^GGAA**WER+ΞpۙpS-̛YGG';N r4#Fnrh壦-&j%,TaT'P<*<^.dGrܺ"@ &MQLז~L~aφզu`˲10P)!#Ĩŏ}V|C /`y)|4F6 :>(9g>ʜ2`=}<>o=1E@N L>Ыl0n[D쏶J&i};[~5~쭣|ELvNt"N~RQzV= 1څz4[BB?ꟼPHF,x(ԋhUEItpn/ߤ:[yæԖC:Bm!ؾ@k}= 7c h,T-w0sFUEZ]_Iq@n$` hO^fNzQITNM(xʭdxvPC?ЛĻE65(CcC[a;xv*^<8ѮB&m&M.R[y &V~cEC#L6]yΚ;Tx#)7cᖣL3|׎2>Cqiz7vyO+ й\vS'DM q2t#fŇ3ЕΡmӥ<)60 ^?-@:TM "c>5@ :w_Z$}V\֢L lU!2F^?H}ۭǰ[$;^*PN?Aԏ B6aJ&6b`ؖ?ԘM'-ZZj )=F;shIC / pt*x_Ȇ]J>WKՓݓq*^huwj{jJ,WkO5Y3L?j2R=eK+ 2Ʀ`xT3&+1׫Za;Gڷt3lchnۭw`F|0$̥ O2-?'e/Vk5!طFZӏvvuvwXЉԢgZI}2 4v5?Ԕ%NɃ L9.I{&-Oُw pp55Y>ie<gHΒ.#:RG t , / yd߮_%X6FVQpB L[pz*do_x8γ"#Y?2ށE?}K\"j IiKcG$ZLe#KU0ph!v &4K0ɘ&"'|npDs,YꍳBá,ivE#^ X99}U/~n[icadMm}<[*dc3tHEfe]0Ŷ%PUbK XZCG3/hChgBҌ9č׎wY̓!Z>+Pu@L~F+\.8\o0eиK$Wo0`MJDj(~K[zzQ&p;A׏12# oK8eNqQC" 6R<; 1Mۙ8qER~> #eOёvuR$86@ډEa5"c08UّUħѱ~IrKz*@E6~ԡ>dxTD Riv[ђ1$& bUW|l.&R v{_V=sIB#[~1]2QYJx:H¦ckozU;[[<ݣ(]Q9,RQÿ)'/_-5R#k%g.J.sq8S j.vWs,oK#,>ƚ5\:1_;^}hij.P Gl̞|Pϧ~P*T*#H6;󩰘+P3 P8^fR>=B;Qi|{v/~ĵ MMҜ%⅍mATUnTbQ5 "{*C::DOÅ &>{. Vvۍ혔@=?ID .~ ]e`Ν;6 V/p=cbIYSE^@\> Yߣy .J-5)P憳XnDϺk\XU1p4{al,| p!?g`x/R}5CJ`]d:}砟5ה[4$,QxٿCtEdvbM)7[kqoQ=|xIJ0)4DIFs&BH/ e U^FWKP:8g NͫN샐.ln7u4 X Ҁٜh){ ;2L”f6,QGI\#_AtYii t֯#3ffOqީ->lUE7 R'+o.*nÅ$,4* ֞2tXkJPe47X!-), NaxRj$ e9竨PX(Gd,]XgaI E.X+39`5ZBҙb"u,$?Vc62V jYQ1Zq^f;}lD@ČYV.I 61I:aUfMe5d[&͢,JRa}v73*qI:ZAϊ/l5^$&Yggu/G-9 Gl`u~-Eۺ,F6ޤLUۀy!~燄ZX2;$ぽ-/mM· atȢi51;kFNh7ȩ͊ )IPV%QwEa`suCj8!HlMc 7%a{az!Ia}"Yu IqXHcFe=s)_zjmc~dsk.LI/82Ur⹐R< cF;PRIR Id=]R2hjb ,kxGYVIJu㖴@12_Ա*:ed($ȮD*W2ySCv*$UkV1p*%V |* JV6v4ִ,qTE)3L[^BbǗENIw=z<¯ XYO?/jQ2>?KFkyK5I oޒ٧,IeFcR\X9-B\HQAG0g',=/WaKQp?8OwlB%L-_ ]z]o`z&$|?0p$? ^ K{ klEHԥ@X#܋iJ_@s[faK-Xt=.CySHvJHN<=* R8m.unI>|Iüҩm{]7^=MnmЦ4H$a:47 !vx oD[($6MBt5^JU' 3W^*!$\scwNv:@cjͺ:+6ـ`!cV)$N8SGf U$$ncMB|Tr\6L~d9C{?7\Z^jJWƮo,a҂qqwXU{W+_n?XGc` W=yH k~LxSmEJX*ARi@^ký;DFp~1 <]ӕ ۙNД`CC(u3AP% ~{Oܔ#-,IpWhD}=30b\(S8XA%p{#00w j :R'{7 {WH* "U.n{<k<SH@'>tv S n`-: S}oHfP)w<\WO pmC1C!$L8["F11L@M5\/DK-(r +yQ݃h&˂ ^Gʓd.!zW,[+\\#TÏ.<$v70 L!EFKx "v#D!$Yg~@8r∰Dpb;z^BtlG4qDh^@ 4Ds]pu*`6K4bp }B}| O''mӅAdW?W\XP%Jt l)..=Ayk^>qF|2ϟca#H9iyJ)7#Džs46S5ܳO]sYL(Q; ~/}.,LxK2Hgh7ÿWAC7b*#? ,:d˶D8Z 盬Y)yZ{Ax\Xaއ}ח</_}kҵO r&0Hޥ~"kE#eᅰj޽ ? /!W?oi$*M(J9A'ƃkVc3 6ߞVQ0^d" B1;Ad{EϖӴR\NU7SyPڡϔQSG>iڅ _dG;.)B K - ! o kG%aA8ࠝVCp O#\#wD̑ 7z6O"[?Yk976ShRmQt`G3?e1AE3k^UYZݻ2ވe:ט\D'eb%Y\8e0&`sq d1J f 9̔Maٜ0"!{-bͷo׉p*ښOiҘ/8z 33a憵Hk|p=hQ3#`obX>LH\6ƚN|C2j \ڒdi:5gcl.Sp&Ղ<֎У`#*#L Z3B|PO;$?Ls#l|&`KebRXǵ y(c9RfF\e@4Ⱥ  >hi>Vx0؈ d. mmONT>f:(bg)/Lド=mW`\[D(c#uxmB7q%_nE/!3Ùu1'0x-Tbtpj/#_&{/"\VpEI+Sq?m= *RDkb.C \bV`VGZbS1FC\NZ`qV˔nO)OoL A~s }\eB@{E< \WCkOؾ5|K vtfXn&m@ -ke?ʀG(K /BPl&8-fo q#Kbaٙ%&jyCM?eyT-KwH 8 Kf+NIgR4vgI 9G $:I,ld=l$66vř6ؿu9hpJҋA9tżZYnAo H%RJ CZ3Ԥs 8#&rLIpOa{.yp:WL7r 4O\ 7_':!S"u~쿔eun'beS)XZ,sY%amhL|Ѓ&U[[zT1ۮC3\joڲan*&ߌl?eޚtfE: P4PBѝcj'mZ$d/2|"PwD:7Čd-eZ<|w&q\tłH龦DLK|Q& UX L)cw~UرbJ&q丅s LxwoÜ/h8eN:n2Y>~\t\bqQğ!BSޫLr BT1Rߦ6Nt-s!엸H8YM˸ C'n}%qKu{>UpAkHQEE|;46Ps@%-_FP3\bk˂;dwni\&= W|M+'|0cln9m_8kTK.#_({;ف\s-7 ^D³\M0W-R^}G:Q2"+򕊵_8p;3Z[hT{T%*3=R՞ILǎ r{$]2gӜD H#8dHfreiVnfZOR>JG 9':o e妵lC:hwPa_͜a# q/E9{0;,X&.&f),~qwcp$bF%0;8|y/$pVz9u:}֕೭mȱ,8Z1#gwpy3`1UO8{o>( _5YO \#x`W/_x7'N=%*ױD_C춠jt7 |a=[sֳn~=낇ֳzf^&XM lɥ ;7vF lDzhZ)$FI&Bsيd j-@H!6[q!l n&MA[pJk_D~KËpITq>Wq9M6ؿvP1HE[<!5w9ӡMF@Wz5سU47d>.0*ߩԌ%m%0 "<`v:55'75Vyђj-[H>}X56God4MondgX8ubɻEޝ3( j]ߠJyJ]T>^%TYYg77 zŷi6l }.7S(у>2 uTQă$SH}yiHAuׯv/A H/h}悦-e96z +.sK@? +5W1zW=|`ӭ0'gnfWT84i~4 u&u ;^W\8nECl&$ߟξGy~3}6XCV+oe4}(ϊqLd 4s ? }|ZT2@ׁ݆pM} kA1uagϳ!J0n)?yT6َ2If~>O'gܓ/5t g&\с{*bg~6LIe];0-rg/^ hفD.}q3[blbÖ /M1U O3E 牢r5MH.Bѥ~PJn:DI3҇㘀Jb&C6*HQ6*m̈́KRƜ.n3;VV;ExͮU3GߠӇ[d9+6ŋ ,aϳ@}Ա'ni)IKTΨf]ׅ:HccjYMW;2 p\d]UJ_RU2x\`#Ո67]Je !D&aNIݛz1D_D߸2` Z;?`k"ϥ$г\6C}jCN}V' }bq^Gl\[_~{>Bw#ԽujR38I!  Dcc,d!^ !*~B`ExF WQ04]-~p;%+kt<'cmpt:3~am-cA{}-8 wwHDC;vOFHx_FxCkDtnX=](gޗ o/&6T6T!S8))fKo\$<򺧒$x: t.H.bAb!~W5eI^!]ֹj{+JĀڨ/GՔ\+eCx=1Nf \ I@:Mx"ZdB8fdeb߫ĨDH5\xtx9xxZ>fpP4|].24հJ/s [}ߎ=_dceL`i<\-w<iIcfpJ N~(6rBFNN S .]<ũ4R2B% k+đZ2tyuql|X#tdҒC"Pi4%S>2:(?h|a*z=ڿ8ȇW@L4z靍\Neɧ$?l7;p4%yǷɂz):%;^h_ \xTqYX\pSrr[~ԅJuՠX6"4<]ؗa*iV|b$6tMe:j5 (|Xmt>+ǒIz17ugpY37=6?{s_w'sHzOx2.i*>TڸgD/.DQFo>f2q|ʨhڄ \Oe#e'|vgJ[$lY2ujuxT2Ĝ(ϒ7R8T,=C>E%/$p]аZF^Vtfw/2cE)dRhnN6v0]̄kŨTZ+}: 9=7HR̈́r4[k[LKkG$y;%# V.X6Г1#M4/GIX{J^LQGx<5;ʑu1+b~bi.kdhj_WԒiQ$E{"nR5{ ,䵪Ц829!mKܴx] ƫ*MW bZ W􊙳ui˼֫Zݵ d4 ?CɗM{eqlݕD*Vvmw۹BwT]`qB[;tY`WG 5Zdpkpa_3[d6f6"ІuqT3-mn[֮p-mk]4^3y4^8uRLz&k7I.w]VAÖX4,6eWtK1fG@mH'_.iNF}_s+`]4dIns&Ϡ͌zQ1rH~\xYvq\q1qKFJwWXkʠd*}8[R%+)ͰB4et/ߣ)WTEIvv {Ō˗) e0pptԘBpˢ= sP{Q))0S|ƎcYuL(eSc|w,N1}bH>tgh/.E<_<5 ,L~(0『ުiA }Z+ܓlG+Q/7`U**uveNN"WkS#Wcv\;^ V5vlK~Sn)~o`Ʀ*u:;5MZY[*vߴm#akyѳ# sJf$v=.>۴#3A&BC8j'F͚<}kK)>nyH9[[ۯs K~` gHF ]k0I{pUZÕm[´XǑ !vKRʓE#G&+O%ߠ`%~ܥAAj{~E%k ;ٰzMӒO͓@Ye;զ]՘Γ-v*C5,wX^}{6i0=dӨj!sI%*ǥNCQE|F:]blP  Ihm1Nxl!l4q^SGr`!qx |T82&%3MB@B B(ɐL dBպ Njӊ Z]QVkV}Lo{9cZA+=]M=rkgsVhn5u@':klr}bѪN)v :X:3H 4w-NƧ2Ul'Tk qu^cY2mW ^v( ]1;68Y0X s5ڗDi||vV?jRis*̛9FJXQzIk\Ie9)n-Ԙ,t[]MJAZq6M]I[Z=mK*9f₆jvUW8RZck]4v--w:y4;*%S)tuu{VUj18z+WyTj;\Mj,NZr=-vkٳt1{Cۛ*9'!_x(+֜Zć H7;?`.!в,y[qeZ]=;=rR*5 dcM ֤9V YWJ']=̥mM5-JC _FlTZm^ivop2_Ӱ}\SH:Fꨄi\θ@s~q~PְS-6y;{der1!IUv ]&~cmаX k4]6ֳѐPqY3ΐ 2{zWJ^=z$~b/R੐1E_ϝ!]/ b\zXo>UO}d&h%sj0^u|zxsv-^8ޡShL3ړj]rc>\EӡAM7F#Ogm(*3fʢ} bYj,v/.k]gͬ_X;kzVO-Y`m [OҠL?0eЋkR93ϬH5+SHoLkMݭvOGRCۢdhFReϺ U3쉜tieƷxUpI },N4Y9ÉD/˜_R3q=.oGUSDg> zsVF@V:㫬_˙ Yj'_Z ӻeU٦< )[ٶjJ-ζ.xBOWgO;ΞnF{<흕 }2]Bx+ Qr\V$Âa%v#IU=~KƏ H tW`6HPxxNC_(X6Qv >Bx8}Uė~M`rtb邩NiRtۃ̅NMc능{*Z(l 9J4QK˒T RH%уCbj^k3hrIncRua9g\DUDO=K TE u6DJZ_NuQqfA O=ѼqE5/t-tP<NXz>}Nu7`e;qg#P]]萌66JmA݇HG쿥 zlNdm,EW$58z,:"pc)m4^d5Jevمm.jϚmU/d!їULbWW3{&}"1yN238mg9Ħu$ӿO )f%Qr1n2xLH;$GE]yDC;#\;55Q]}KzTw,IMt|Oa 1,mSwatGHc5I4j=XQF[31-j"L_($Nc Mՠm'"[oR64uu- DuKA{oiR}FW.&=Y֎R OԊXe/$ghiY oɋMY.IO.%En@Di§-] ou% 4qS"&'^'Uk2bh̬f-zBBDDfr[#GO}pXv'huX)7L~dpnIZ5f"h=FҰzez|b%6UB;ظn:F&M %A$8oXPZ<웄 kξ]D?d,@{(_$5:z˥:^ro.ͥ]ZH.˥.Rieij&n>Z\P;4(veh%y $9H&M<6ݮ.OB˓uJ9cXB0s _Ȉg_ 7EZ ;& ǧw7u:l.+-ܻۊ6on xm^ogjt;$- =i$\ tyh}V;!ϒ`/,ROS6 ׌C)B N-#`yDSHQ,!5g@f%$ҖLtXa蛌/b&Yh֌#Lxzپo,Ƙ`RH$_9!swW{ E Y +XŠzU@od$6{LXC5R*,x94c"f 0SXLMvs?wg?ìGdDa1m>M~l /PyS K0sfjf-YO˼zƗA!#KV>nŞ6ر%wq"숭M4e[u%Q9jyz=nOϠ& `ˌ]m/=6nXN2@] '+1@g#W-Lhݱyp^8* Cł{5R*\zΠu=\R䪨)Gvp[0"R_%fP!Bu+\xSQ7:٫w { <6o&YQ~`RvHR]hRW Uɯ;xxa>6$(: 2FL 8RrdÂR>$nC" &b #^Ho%]P(^2="RQH1Cd%4OH-bSvQt#fBnqʈ=8\RTJWr1#0dBz5>a6i}#qnʼ"rklTXر+3OZ\<vFk.I$Tj%3iKlO΂)mόB(BYRL?ܣQpQ_](26aiyCPYū䲐|ٯ#oHѱ$|I*u6ol_S嬔L)_A(6k蟟+YO}T>z %Esz&k-}z]~nG%㯢@ܲ"X|3kMRIlp| ĄJ 9͟!;oGq}KR-T?"OIr/b9Zk!v>twyk[>!z//hi"En#{>))lh$*e$p,lpIr@B.S:R1'6#{{l<)24-WY<c4k6 vDPcx{1xewV\?iFqFbC+RX'TJ`Yъ|m2d/&¦J%C~W/-?} Сѣ $i`PS^GR!HW?'&U$>ŕmwٺ0oV5|%oIb GqA`%x.T˪Q:4&6iDzHD)'wqbA:8V%?yE,`f}.=>pHa!(i2Э4$0$6,P:TqAHtJ#46tyMm8-z8 } >%ƀ]QH4ya;l&Q&,}6ФZ)O\[~.ҫcuQ"74"v2 ֔bye\l-@Ҩp9蔬B"y^O]gΜAAٲQֆ\KB;G>|}o*iRkkyf6 1mg 2/SppQr3GC.,5}ln*Yzᒐ`r=agvVWS~US%2r k !y ON;pS O!9Jݫ]zmw(O`W#|V$Ӈ\Z hIk<+tg15 :rm1kȢ}>ZJ|a4*l%iIџU^YJmVO.uO.DEsG<OcL­qD!v  T*ēRk#qŘxWCg.sAAoӔ[T8*Y6>\+r3Bp*AK1hշ@6Qd&2o~b/s:_m6}pIicZR6$ =ƫI.OS% $2# 4ēDzӓjBӌ 50 aCRC&XL7עy ֟SuI$QF${9vA[BD.ėO~%%yQ(3BM2(ſ2pEZm m v^00B T|BYDl/slNՓTI )P<|gD9KP_֢ؓrig,tfN:<͡#X˜P${%\Vc-2J >u[J4/KU3|kYs]gjwb,Oh$JG_ζ#HE28c$ ef$12R(ʥZZ y$8C$FJ}gئi̾ ?s7/~=1He4֒ T8';, 9Q[)iXF3Iٹn /QČ-]]u{]MLkh_4w xg6na,е,Bh2鴉1Ցt2p/jKW/ 84GqŀѡɫMH&CT4)+ZvP-ɤZ8_IJRSHFQ ߸qRCmx2TV;F@MO2xX_8_ 3Yx_ƄnMf:eO~"@kP`by++l Txm cX`q?7xEd&+<MEJIa\ۧ4{DHK%D+iL趒|8R XbG{Q~N&ÐAA* )}@I&=%ᝒM$> PDO}'qCO"y{v*WSy\X4tߎ=8SP{3g)NgSaSX^K#$OOTXى*D!3)Oq_+ux],ޅP ?$vIBw^͡ F`\ U~RAo%fdТnE*A,m/S[I먤;q-ZU0:ř˼i(*l [5_3O 4HvꨁW~u"+/"%5.:IJFt6q r"Zh9K@ /(H,;x:2N,_sȢ؀5Xsm 0/#BPTv=@0MUkfLj gz$Y9woImX__{@|9+2 w4,"p%kw0#A} ^)&?N Y#ڎ, ? ٩a-}6 idc1֒DНD׺ ^xH-s\I~P0@a`z>s8K ܆8hAHbcj7i\e oC6^,}, [1U$pBI xwFR~\cD jKq =FhwD?Lc8P D [mBb:E 0苂w1j FT8"՗SQS0CEU+t ?&Hr IrWsGu]X%Ov#(TŽ(\.!z\6fH وۖ9h%0oÚ@_Հ "el^5#uL-^Ĺ|Y,r˭R K!y$ ٍ!ݯA'xvg(Efo$ _e]z 2?G z8 A^ub4H2D WI!"d=c2? }7ۚgb;A FyJQ%9_ieN'jm\OLszC l7#eA߽DWkq] /֓|_.$Ses騳yz3$^L 7T g~! \! ƻޱ往0neXIxk ҡh3;D 7PtTr+{\.CHb vxY׼{$9PB8( !A5w4,;tco,؂!|<\u72O!6D|3bP!G0l]IfۃW( >?C;2> ;lHSd7x[A(_Bw<> S3k>ηdTF2u,0 c}Ķ%%:BXe z\NRnlpyG*QoI*< ᗄ|8cM,l).1,J0EpiH‘= >-A~M#4b)oa)1~PrswoIPz Gq޲AUl{ ѫpaF@Q媆[Ӆw]QX} b,*vu"*^&s2>,]CCs -=:ΔV:~,E@rGeH ,\J77W(Ჱ|e:l` 4+C٨c qQTAum aPUg! 4:^BVkn &JIp+j`Y#U7 na"Hí r2*@>1 ]帠PgP 0w_Itr [wxV@H?I̶#R_.3cN'h:Zꧻ.>}…L{j N&H V|)ɸU E8̠Q)feأ9A(,©?Lc"^9W9I1K@#$H2+,Go2J$K"!qCz,\R#!t dJai'1\{I(y'Iv)gHYC佅vpo~ٷ:4axiH&~J )~"idz8qy<I2Qɂ[]B$YjP jm>;b`|c9\ILnDw7ə-A*2>?N(2C"QլaLҘ{!^Fyבasp$]"('r9QAph`H[}On~IR[ 9{gb [iʪB+$+Hi25O#IDO/85xd1jxLv9.JkDs}kz J?kgK)Y$ [MP,)}!~[ Qk8ƽ 73_&(eYQb˨;+ofKVΓbjq`%6KCoQ.%Sga4Oʅ7pJ(߲8kHy :~\̻%d:UKQKXgC)E/|1y O?#{K7 H`BIb95 {|vX3!gԀF}mԅUߐN^ELUd{_Aݑ=KG& òX#`1W31??{tADf!OoM@ /3C"\\ⱞՊ|3Ir ghGZ!sd#o,Ś_0~VO)ǃD1(EbXD~14G${uҿf(_?s kc4- ^>ͻ`O`7*yJ MP>39ގ }E dJ1g *]E2#7 AZCmwsRQ[l!twsˇ(ȶ6 .~D^,Q;EߋdH1]qg2;DD iDF:}$m* oQjv]ü.X.>1 = =({ڸiѮ#9rٸV (=3*`gp@%N@8}ap 71#c~ ]1UovN%9-( whi`r1ohQr$Gi  6f {ѣ>? :Q7΢m1Zm;X!kV*=|G.IvB#"p;և )NLu`0IycBNd/35>alpC_PNB?eqGzL8B=9~h,~PΫ 1@^Q#@0d lX/ 1[/',%@]CglI/M$w0]to,EQgZKxa:aPԱ* o-Q' kNHlDt4 Gƛh g^LjU&E.&y*2$Y/4e2a2E&f2ZeQd)̟d2 =*A>))D/21B涤 P*lPA bBКcN%HR71Y')tln⿕ځcB?r\h_\:Md}5.q[&G(ag(Zɯ%~9{Jq$N)@]$0I_=iJpEPv&}gE:QN旓"C;P*]eBuaO WdFihОR|J~܄<)H4 ;qPgz\!"wZB9qzRi\Y,]K ߣbLP$NKL maZ&+(_(\R5knJȹR*l =bEza&2W+vU9Q0Gh\ ыȢg/Sk&xyIޅ.2'NuS'YC~K!ЙW"J16E&ocC8#rȑ.*2h<FnOUSIlhjm5|$oEn]N#zl\LjyDdڼ%~TұQ& \g='ؐo?X3I? u{(8r`dG%W$[BMz15iy07b܉ZF"_;,*Bl!.%ƎYo5f9nP-2H@CDswLLC)X7,w0,oF }ZI;G03s$dƨUCsz;*%dw0H eR:WQ1@j?2<#]sO .zf+҅aٯuJ! A6wA&/oJ̡$w pb< X}N.ECh#6qRnWoǫ7-}a͡.em<073M[ǦtϠMtWJ W"FxP)ְ]>Uqp2>Z( kP0nk$ȏ{Pp>!g>"gcqLCi o:f-'ZH қ*%uAS ~pp q#'Ws\EP0JEhV {g DLECx4A$A:!*ȁb\'LRYvY 8Li ͔Cf?$6fHN4>hwb5p#߉Ky2 n)!J4(ɓFB>]+/YH2lٌ$|g`G݈*؊r 5V QND܂;Q6;)>9I %'|TS=S!' ~0Jrۍ K}îЍƇPxۉkzQ94O\pra/rlýqJo39 >3ց.meW ,a#=t_)dL%"?H"2DNR´UndUA{} ۤilMD_&-~mE6\Z:və3eLV=ޮu5hb+mgݦ {XLdz?I~w .FEh_  cHeHۈN#9L)" BLl m#rxp`\kh!ʱs zPCRÃ~o# M8* qQ!~l 'i,Oh^ !1O'Or)4w1l5s0'e/%y>i.NI י z[~R~+K1[X1YRJ(=U3aS?j\S1|PotO^0QkDϒu-nIpS3'q$wDGF~,My):@"S2Y|0EW-8M N?%l #k5f'º\!nh],*_q!E\?keQÀ*ڙw`KBu30է@+uvILgk`3bl{'u(d|OEzP}7/CJh|wd 5J-{$is!HPϚo[if[_"/Y'=Β]l&,MRNqV-0|o VE1E .*a-2?弇y{(NAL{1)v ]V b,}+u pY" VQPmk 2DP셧Yd/ȾK_Q 0,IsdbmVHܭ.3퇢X?UqSH |r[S{k砂lQ pp# RI!D8 oO? fE(1 )mbUP*,`& kxMV}3d5 >^j/(tU~Ͻ\;  /w6(pf|VhfW< C) VNJ=~BpjV1OljτeX+7>q%Eؔ$eNϯ!'p_p*>|IUXad35vGPD bxxPƿ }㢶Rady~%i5Hpxd_ 7OY$#o&l3ptߠ6cĿ }QJ8Dı Hb~B\d 9G rI(ç Eۢj7{;L-csC[O{NJ.M7f]FĢE(䉺aM=8UPRaz1b&Jm5^Ł,Yhg|Ft tD x] {JqB|\r7$rYinhQXfA"64Z."O(dNrw=vNJ|v8)P1Nԥr?{WP>x~h/dӽ+pL/~,H/Bnx>\2ʳH7\9AL^q0t~AJu-* .X7ʏ>!F#s$Ne;Af$d' [?vT}$>•_=훬t's8:SX_(e~k{?aX[XX"O:]AҐJx#M\GAcB=IڂH$H7}*ܗ'a3Lü]JpuTѮZVxiXբύ(^68YZ d yw$K K"ТjoŔx=XuPhbj5gؕT. CF_7%d'4F /< |SVS:}II5H6ᇓ: 3Lb 5L6-3/@7ư&o,XE^9_(Wi~B80SQmCFم(OLAP%vY߬5Z(tŨHlĮ0lqϘnE˭I9I($?ͣ>)XGᩃX9bRюZtSq͘ !iD|ћU&DždQ,%&qߍRsKG\1Ɯg0"KO{.'-韸o ǯa?;&FfBg=2n=3X?_loF&ҋ'ˬPV'chaIj mg[n o!@l r}"#s@ȍ(bX"$R"5Im/B.S@$?!{d7 f*yti')ڷf+PcQf,!SL;EHw$Oewh d~NmҶ!Tc{@Q#/H g'ᒥC!63Eqo*;+bk21g}i8ػL̕vmӊqҙe,< 3an1rA8+b((]+p5 8f^cC#\YۤõMbUf:7=l"nkɡ'vEH1e!q;%/k9C?]r[q%̰I?8Vd/V {ul8U8,3݉Du̗pt'w/3%)fgj>WrqJ . 2w s㏳F_0-ZGB{PgǿGRPv28^q<$o |#G+>f0~xdA<?. o܏ɾ+y@ܢ|zQ16Qc> V)QQFn87J7̒lr\)g68lgݴ㣪ݹd띬3$o/PÐB$$! HK߳j+*+"պETڪj-jh sw2Ysw9|y/S}'%T]9'۔o;m9E;R7xx=5L?;Q,Sg}{gjJV(xW  qW=+*187[P;ͺ Yqff'q9⇺9(#Dcpiw=7/qTG7<"?&.2^9Ju:,W 엃LtWbdf(֏1hX#,GW&EqW^WKB>3^Sc^j(LzWIsYb'ۋ{[=})B*L~|~PoUEkʧEǢ q^Q7Ôe) 0g&u{%T[dž Xz ~u+5׼ mV4+fOBb"Mg#{O\bސ֑u[3HhXuZg([mpw!\󝁾ߋyg5wJHf ٷ'q_Ja~f 9q<^ڧW[?#d}+  " r~?ԑKO=MUΐ6 S Aƙi33&,xu2J;y}ގnC9 /Q/9$~qFɨaQ Y (L:.KEi#TBl\J:ò&f;y!Cʄ An5eV@;(p2u,뀕<%#%Nj7XZ<"++XXKN@$Jӆ1pdfR &Vx&*.;7 9Yna{bq&+#%*XKq^2rTAJU>״i&@-Wp/IW V\]vqǮ셒4R+2T1qY@*)~Z+f%@r9Mj[-QH ۚd;BR;x!iW0fZ7&rXx՛;$f-HQGC: }{RR$&z^ /ݏLrnc0p ߦ2r…rr΢>}&CHGdF{h*%|~M@8:k*0PU%ZI☰m{p0ZKH3[E&pQYE$6)Eu65ntofwcrKE!H[.[zpH%aj3dK*(+ِyג9^K&O4@tu3_r G〉DK[=dqZE)eB>Dwnش!皘PmpO2]If|͖-rҤ #+_&OW[{Ow?A]4X|L7{uPH P X-1;Nm?*U!y"s¤xy־Az r5}˅<_B`&5-j")2]xgIDy*zHzm&,Le)4xpm/a(}8̆â $'Nל)ɇ-n#pTݒFMʷ!4` }6'74xUL6>KU؝b|2|Ϗe3`.ﷁXqrX7oK=&sʑ"qc NEAtd!]݌ɢͲ?fػTf٫J =S'_K0+؈!%mFnqE%er7Y_Ne1x*$x0Sp]* V4\H)|tl5SE!cTW 7eMݭX"i ab\7enדuEC2xƒ[Ȧ wqtud+)fOV<-J\9HeH _n k p"k`vrr"Ӂ\=O/RTV3 dLA x{+@#M8.o*Ol`-$IF[oA⢹SMEA7yU sH.{fBE*Tbտ5Zv"K{+ JBM>Qm>ђC7_pHw |2ssںt- [3-a"Y-OJByD9 9ӻmaNm ;1}(%1qI.a7q$,}p t¬p1dkw3f{vBc{pct#!f_u'$qxOp,܈z *4ШWPoB]Φ`aĝh(s0=6':v̝Ij<͖:%2e dׯ0ņS59[=I OO\]Ym-)yף Fֶt"Vm=\{ˏ|+CUhLG_FѫBMSsH,T4u XXc$bOxa7ulqQ.\VXr 𞕦+Ǭ o[i-$zX&(ru6[ LsL8ϕcAr*8r%&D1A&ݱE2:k,qXڻkKk )x;\:}=M`mKQ&=I1[$ѕ 0k6B3rks"H$bt,U)/Qڤc&ebTfM #@V:Pr^xhilVzGJ:r[\1G\QV̠e4Oi`ApƆ4N\4-)U ]-%MQ- =X+^X1l? YGPM6 Lykҝ9Cu;Q 3."&{[XcTxNHu!oqBÙ1|Vm򹧂ț\[Z*}i@ڳ)hʬ8Qn>£v=oe۱9~{tN8QorQv*$p45& YzFOA=o(Ʀ8u k(w4`ӓXmk=m<4k=CYIFɂu[Zf|bnҷ>ufoҺWHܠszA!3(( n[R*D?ea8 2<{Dz@9܄ MfXPGmV1Q73*Lۂ\;^B %*XJARZY :v cۘ=g2ܡEOnpeC+?QK=J`-<Lك+h"t|]H444А:\o`(>/-mkpT*KVxq [AUXH/wxTx {~gO[~_H<>ye̜Ʌe&#IN+'#Brs\ݹ|Fn !oanJ),8g:&f{2[rMr@sbWg,u#o!۾$E@Y97c6 vcEOP}+ƚɔ ہFsL,0nb+Sz:Pz9L>hnoeb@n7@ϛo?y-LL<ӟ\f!}7gCQ.%{CL,=FhLKf9~1ɿO9w8&˧R/2aza"e&kW}5y;][EWxY tTe>/ITTe*+aq0$aGCփh n+\AsZI:iqP AQgd3=i{sH}BS{!]ʛƔ) Z*kQ|[5<2Oˡ>s<%x:56K͕g]cq);ZZl.d{GvmvRq{\t矑;똯% L*ps6de6?S}sF[mvۤx6:mobdtʱ 2޳ "D7bU!ĆZ+bud|m:,~1EI *oIaX?Wb.fo#$c.Ъ~wD'JX}#"1r2$A+z2, 9 Ѽ‰.ph~o޿\8*YޙrB,/=7€M2G)Ǐʚ^w}[KRF Y>ʁD/bY>% ʼ@:7h| ~N\x^?8OViѵȝ7TkWEIY 3+qP a)ӦBޢv4o*Cz,Gwz kWhlSp}F9wlERV)5V]vruB?NX߻!4nfk/PW MJݲZJXx3WyUuYfčipppCKh8f[cS,JݪX\.F*4b]行~홐a |l~`L 8>E:1)|⤘;Sߘs )BUjzA+׀gpR_(LX?/|T7֗6@,>;P=yevv3nP#{`ӱ7Eڃ>NA| 9 psX.$&-q8'HY~<器lw^wʯ4XTj3_qˈM90s^k5TPY™Bh ~_K⥢nJ=yj{\k0qz9K/>r i1#đ7=y*I'dŒ%9)'yaQ#Z`2E>-J҆]eʹy[&LCTV_[TŨ6g0ꩈqQBx˱_'c}`"'Tw߮9wiƍ4| FNN!) ,#,zJ?:V)2&5Bl9/ڳa|q%P[k,HEϻQM{UR?E`-) q;8y.(sV,Xj[[3OZfHϪ5?~<˻MXZR,sS^Մ5+,V[IhwhUR\6N6FDgv@6@X7=L3,~E?Ο<1+HFg } #9~8&ϻ˸n"k$N:_#Q' { 3pI pXe"+br/aP_|AsƨqRbDG VExIr"P,P/' 6 Ȓ, 1DPZ}]ya`~> Ā~`gR$e`b/y3l_mx!IXDHDrs18wpl^ǫy% i)\ vBrJRg@PSS_,Drr<'LGQ5X4-EV! 5O#-3'UG?>%_`WWk!HƠ!dIK/ɀ:((R\tHBRiZmѫ0YXYb4am0㐰0ؾTPO>#YY,dC%Ͷ '(53sfM~{0븨XؕGj@x"4(DEy ),\P/:]Oxn+"@8"Ak$xbW$ȼ:60 A/>3` Д$Hu2xqߵs ]Cʼn~~wˢd &XMb4r}}L NvNBNb ӛ9*pfAZWL?M"Ґ? 0= 9b[ -z? fU3"/|hMIlheCm\4O֪ZzP#J# uh$RSޚVUq/vLFME]ZkQLϠ+#FmCwtEtШb-k7QcPK^JR 3ן OT@k<` >ZrԆJ!T|T ,DÓ%J'Ub!>2c^$䢷x$ہk&|xmg 3'a]GNeB9"9EE/頷qJ%6k ZU{좘*ԥˀON ^9 y#|Lp#+ZU sL h33~Af+rp\Q'>񡷫 |F QNp9Vtn~|Jܝ=AI m6Bh( {|(OAVrnCBU0ٳú Z\`vR?q `Xנ=3".W䓆T/C .9^;fPpT;T 7^[BbG׏/ ODxfkY? Xk"b95s8-젅P y[+}+SBQecB1 FUB;BchXԄ xO)v|bK8.+M.ث%ʋh'tٴ otDrԽr,\:Q˜#N{¯95455mPt6x}n@t|=.ޚ۶gU5-6Gex.] n*V!LN+N;O 3<Cn!}zoڴնd"N*L'o?0={8)闻LgϬb;Á`~)ːgO}I J{_AØПNQF*_:jO(69|hsZ*..Lf' Ƒs̾iZYHtMQ.eŤ} *ѪϘ/cKW=HDnmCE ~t@0+(p2q ILB0NV˭ᬝeCIEM?"! ^AllijAe׹Khۄ*^fJr^c\÷??qZAQTJL8Y6>>eN;2oy U%fcjrP<#ԇxXWb{X7Uӟ6ĭwn; jrOщ75 nyoC7y*~ ]Vne,gHkd=66Z04p KYEXp9=PT}FwҪ$w"|8{#c+9&3+Qvq}, Bø}lܹe8LM퉇Zp[/, (z]f|M`ON`VćRrGR~9 jR8S5ºZR]y{Y3J?.d 3Z %0?4 5Za?1KƳZGZkLDO6ܣF2J X4T ɱhҞFrw"tX&VT$l3MW(s*vǹQ1O2gFt~Vv$IDez}*RlXQ`VUPs4gfzLʼn]/\hy1>] 'X=#=|%X0M!pF-Z_!b3 B[FΡӔ?L;g12 IAAT?GYgմ4Ǐzָ0 p4W“iWIbDWރ v/`O`4oW#+,2H E`E2:Y1_E)2t fh*"֪0aMXք=RΌYQVݔiFq4} FS\ھλg0v4)_kH&Bc] )V4# 5-ˆfGUH))C ;0=݁wc켻.!qM(IQOyH耰Zwa{/^XZ(;ݻ>GϜab_5IJqH2K/,i 9hNp}#Y9:ur|jN d"5Z|]:9;ʦ6Rtx6m?`*"*JES]e-|Ok0%%p퀡'4nT!TpQrt!0aPERFP=@@@^_-渵?~<')84>xfڔG,'M}(F-(̛ {8Q 2ZH/WKėDt,iNJ)^}?R&Jvmy:?2Pw<֍o}Ҡq&ƖRcYi.~Q}ސ +r@1W9tIJMn}8[\ְ:S./4 ~\:Q$VBUĂ%?78SDğ>^{\T ~*&c.@ gv((pضk^TaTycw霕jE}E \,O7pbza.Żlş~Bkq̞qSOQ+fi 2|~F)% O@GJul/+nj.^]UM3>%;fF;Zɱ_"s[.p; 6ݰNS2*'REEl5]~Xw}\߁ {C@;[RЄ؋,p/P Pj?YDD9sNӓ}"<ͨ,3OOG|tB.CGy{.J#Tlr*CYrȵQ2ޘtI&UJS؋@I^ ?^=ʐh_/KpOEC v-*$W t6~*r"TW-Nc$ܑ̑w¹}-Fzt.bKt/h4s%6)]>ِSQG #ϥ!#Ai 7^3ZFvQ{R@g&Q%q4h=B?.AZʕ7,](difel`3#5rO(KA{/g&YnA`imt)O=a{˽7sy1=Żоٷ Ѕ[|ϭ k@p"tԦ@,{ YRv\|ҨJ׭m#dg*XRZR}*A,:&XjLE~ա RҊV#R+m*FV$8-Aݩ+>"*g&0Qm>}EGTDU8[dk%:Gu(뻕h,"KXNꄸBCאe%U7)OɃ% }^V],!+EÊ,σ:,DNj.&2hf P&4KW^XgTV!ÉyIs&nC(1sVb,9WkPGs#qɕ"x-(֡KRtVCCgEYL5HU*ܒYCזRIl 첚V ?o0|C٨C]xS}%hlk|D IY(۩fd>t6 Zu&Bp>TS4+NoqX *J@]㔕ȶ<\T{Jdr%j͐|g%H^'"C'\Bm~!%7Hf11x9i!v|*5.iKV' {[)=ciO"ݨ|FDS -pރW#=b0z'EpdE+H k8QWIJ{z@e ً3{{^Bs <6pnISD2Ӯ>2 :Af>p2ӿ(G I34u 4HnEz@̊GrH! oGx(=MnXX6v1ynAf/ 6vr:*,q哿R2 Fup=߳ieI{n5p|0wWidw7thVg"@ay<a\ŋE3N|{^ɛphJHO8g<, > \#~!eD%IFC.g&&LQ$9F\Nќ ̙)ltE5h Ssf̉a[I8ċ`" 4tJdiZE4BSvfN<1AGncJ.ȮabJEB-qmxViPSW YxAbb\P(Bъ(LŪQG‹PX6=μvOuѱRZ Vkےa^νs}wm-}ag8<93Ļ cqX&j60?^pLGv70<'fMZ{(>gZ1696g qF~| .0 ;0[]38"EۢW˶t>s(bVT?յJ@Vsx^æ/ԨP^ _i")|?Pҗ`j@^hږBұ%:AeSf{Tp}4t$Bo{TD*B+}Bgº/NLP96<4Aϥo T0vt]3$7v`xXGD)e,}no~QԡqPKrgG z召.24!=׶'Q\ArɡzyO膉Zs'K- {8ɮg߯=Za'pl9 r4?W]0)8Q`t?g+z2)qRzz9 XrОGKOҥrGN%ZCaxjm80$̵=,4@JҥK χ :U\QG0U&8w?VEJE*?`oᇥ gaU|:S.XZW= +H$1-ʍP Eʄ"/Pl 4 7}6ybL2beϭ+[#a[ޓ ^"IRI\wu49U4cYE5KfH%DLu.mVl1)Q:7 rd7+QQۿ@)# OSYg*F!]H{4({u% c̶hQy*x!iT)}{4Uݹ^x:uC4c\K Yz/mh``fbYP|)Ue\䢢Qd0 `}eI%95l8:ev"7onLNi =aru"6x @xJ0 2004/02/02 16:41:54 bjking1wOURX ѿN- Mp)QГ l|+7üڼMh?.ھ6."Pˮ`Qu&hKDxkpeb&QC#}#}cC+#c+;'OʓPRxUyulx;|- GD L  M 7bm ,:x:uC4iꝋmx ֛8:?||e|FUF Qzxk}!IHDHX|s1qɻxl>ʹ+-13'5E/&Ois~3#m~ί엙Qh`ɥŕ9>AA: J) 9% % PsTsF(y\2M#99_wD6glGn^YLm&+Hn~+4YZ`rqTπxGǀ +.N̼̒Ja%E@gdŧ)h k..δ" PH+LN-V25afy = ?URo x:UD71;5-3'a#u{-<"vHzz'Ԋ4nyZӿ3([:q3/8!h]@-3Mb_Zj>g y\W'+('3I/K}O7E_~,v/]j2tJ7vQ(^OڋK&og mS]xis;b. rd8 d*yRAb5K_F]Zծ#===[٪lm[<)y8v1&НY"ǔف;].B?Ɣ{! FMŜȵ-$u`)DR%y#ԏ4rϟw< IlUp\2FN-{o ),!~8ktS2@)PF{4.' P rhK{~8Fގ~OF-j[F{"3IqN~_4O[s =]8TEScRB=U 8v8mLf[liXZa5t\?m υt V~aDq/槓 y!Ű{<ԇiOt*-fD״gN[310h䟧S]sr༵%*=JQ>vL6a¿=$bI^n~D[S'.{곐-FpM-hjI7ui'im6R?j 3;ڭ"#h&IZ hj^9-MB0ȴL)1nrJ$[nK`C b͹T\Te!;0fb羉!G.P] }&h 4o wf@~H[ӥl&UdRhzHC&5u9:LH;QTy3 Pٸ]F4``mO dcEP28ѸFoE-5@s$[tbt c2FT"4q 8bhf>g0_ʦ~D6Ȉ3HY֣W7ZU؋nL'up{zoF_{108D# X:;Ι‹H+cF'R8%(h6o"ɒOx30ټ ^Wl5+}"?hULjq?NJ } } 2%(rcU"BDQPRE Џ.O/B:#1Jð !PL _^Zd(tmHj44beZ \2wCzUګj{1V@E^hEL pPK[U)VS = IJss%.}>"ES:̓A*)}x=<D>ǍMKm@m">9<9Y9"}ނ$ޅ $50^kdF$Bɹ983(4W~#kVHd>H3f9 u՟Id ej )cMf2\$?I%!vp3Yr9,p@9,B@+r(B/ \v*f p*N~C<ǁ5k\{Ѱ Y?p ö %4Ia9Ⱥ퀀 aۮ'i%T' $o+V!SJԸu4!S}Aǔ:)r`  |L-oPv.2#J[u\[<y x_Z#aɗH%|xM lĒ51t,F@{8f`% oo45a΂N5ttVB*O鴗I|2UUk4a ->T9c\XVK5NѠöS߮ -XvTJ\v|XDqVbۊIzD>PYPLB񚏭;M|[Jur_t hK(QSilwyaOJ! !Kr)W\({Fʺl<ŊUYZ#/N뇠r[Elxб >ܪ.O&So ˙ PaBnEʽϨDXB M+m+j*E2ٗύgvǾkiy58GC]YzW繴'kB{&Y m n0IK'm(BFƚ:aMF~PM%M̓ lA.^<n+Wn Ǘ,+:)W'iGi y"0g [J:)9 =O&@ۋ<'N5Ց+8VullPk|`$ۺъ\)~:/UHBIŖȫd)K⤾@Q^ lשϯ|#륲rޡN˫ ]&HIRQw}SRi){ʕ**,TVGT4;+Ych>OB}H`@-IR _ NL[y(Ec/Zt=Q[;g~r*a):so ~g=;>C]_ B3|K̯ID۹ENRpa)P р=h)^;gl$ۜ h>B;)8e `;n@PThHbpG@ ,!aHO *y `'FԜo9+[Fq@ΣԳ0'^'ɕf_rxռ&ej9""%zC7"3꩎=.vB(}l:6uk7Pj[KDy"H%,ry,RhJ+,3,E#NCAUGMA2(f}2Js$h_x6 ?̙$|~^xH<& 4EmGTf#7zev܇4Ժ +`1]˃i3պ c#M+;y^x;|&ㆣLF F&F@`hfebhejRkY6 cl)XB$j&x340031QMNMIea?E^zf2vz`QhHNv]o\ 22KR^hڽ_wAYD\3*FLEyiz Z#xq -9UʒΞv@W%uaKxc(`8/Ip_ 7x,% do:|+lC'>|4,Y=Caj*-9h45G+Z('3 FM92_˻-7FTۍ&aa6%ǿ֜<>LyDTI (9;1k %$ro-5g?y؈4$uP: e O$9t|rR!|Ɛ+.HMfpuA*Qs٠V3ܼ)3â#;%|kԢ8\vS_o%n$}}>:_,ݐo~[KB=cҿ[AwD^5|7~^M b5iEY&$4>_y!~ESۃ>q4K}S ulYDiPs)X*i5 0yR57RkI4iekø_)+/؇\ű&yD30"yg?Yl\'tm&6[9.΀mڟ 783l d[CLHH'ΐ dgQ{s·{} K vou,yo9w&Z鵖vOnd鏇vzKDb ]sv4ݼ9o?ung6&wAkjt׏es=FI4mgî3t3hܝZ[uI:tha_gK!hp68ê>1R/}F#c&kܵuZO/ϡ9~Zj/.Ν٤ufwF݈݁CkfO"3Mob{u'g:f}yZo8Ims9æ#zj |_ 0h,,+X?d>+,:6rLU}7m),Phl?'1a4ipr`Lŏ~V+S k.Wia[Y:cb`R h ֻ0GtV¦J@/'&jZofo,ioCmR]mdZUB`6K$윚nMBW .%^|ʠ皕LaN 岛fD=r¥X$pX$~Ewi;Ve/+3W!uޞlaU j݄Q 9W[HoA2 /Cu9cS8KjSJV=5, QaRpuJ{m .+R[Tw<c w 15p5F`]A'hͣ^ 'wTX٬.Zq8aun)qzi ֊pom&/q,%>;xi]m>`x_MgڌQ˱cXK}S]:6Yk}x;|k˛tfuKGgv-xSK X%?.L⺰rCpCY$q9 Gt_AD b7}e1u}Gܯ! 43'qb}D`GQߘCD엿*50^aAnQ%ߣѐG1>>8.f VpoVVuC;f{QA^%1z9)3#9MʼnrGl#"҃Bb.Cq!2>$uHtq58?'qؒJf!] qh~U'@+#r,R=qOZa\=m.*2aUF k=X^5JeUE,S'u[yQaU($1^ڝbK SIRO:R_*pPi4|PG$/[K&iN7 [,Q`e$.MVKj vޮ,ReI\Bqiz⡬\qi,Џs,F;Yq}R2Hry,A6h6ߟ^tB:4lԣ1z\fB>Wa}HR?uy4^<ŷJ:ץ` <]Q5{{8d=idxU;H5*4:utEQAXZŃ*1Qqҝb)PTyy|+UY 'B\ o<9|1oqU{ .!M}ksLtق t'dۢͤiZ Z(Is>mHqMfcSϵW5am:O%Y.@?e$V &ҷJٍdSS҄ޭT-0AP՚#&tfʒDjV<ӬZEYr\W,b Q^Q hY5 2oV0;9J"-H6ZRQ}}ͨH7se@ Rl=d>Zn[_ LġZ@p&TKPT]n n9.!TXIc%*BHw) gge~>-YT fbZ @lj¢!&E[imIK.W:e,j&,6BT3 J,chL"4C74IvB+&3_frpIwFqJIU4>si$]|To<'냥e:RM*PMXt%AW9j:K(u@VMVSkue÷.U7´ɠld2&3{*%q$ M%q~_Nٰ'#fB%Mӡf0M#l_㼬9LQ +y i PNvזEE_,Bskڶqh!<P lFEM=*t|3GidiS)v""ͲTFC6O"Š 6r0G }y.PCD׽Ja]h,ԁPӡ ؙ&'!7lAȖs2;!)%{$sx 7,F+&$kݔ@v$]y"芊IlA ǪU I{ y7i)Qvr(~mKO~eܛ\KØ|r;:? z7VQ !IpcD3tL#iر#bbu2֟O'bV2˰W,F>' I.`8h2a_m9q>d D,5 GH6N70apu{'!#ւj4m4oc()I,ځdlQfJuX [Pnlm[sv ۚHf+<[*ghI.GGv@ahm!@2cJ~ [A*̧dn+SClFs@4CԠwP/A-0;VҰBV#y4&%Sԃt6aTcBy6~ZڮσA>M97;u mR UG#g;QJ[I-HǂC1C byϿ ȧ\ ?|:NJܩmK[E(Mx Eb5^oj-YC K!> d6"D?HTvX6Փ:tN Ash h2Osb~>Y6ƫA!Bu"%ʰapu8:PH%Uky(x!ey i2|cCx,`d k0?vWjQ 2Epn`~> cX>*H }`~~BkBa6ղ:k_0?_0?_0?%F̏)%6W?B HOlK|3G6c~ÒR۔Ɉ#3 +H8A΍Nio0!Oi᷇ڔ ަ q^&s.%8>]C %!H`U)`Q:u[51DZU_^;Kw y,Jt"wI/ڢ FMU'tEMIFѶ+`zo-}3ڒ\46Ү2l+43鞮%{H.2/IKM[H \˸(e3֦e^0ג7_c%]HݔV`fjn*)l3}XMEg>)H|nTO1q6gmFK*\k\}vLWcZ(r–Gy>JM+K0)̸%2?54mi^sR۸!YnJ&y@C`dQZlmX]66e9ߌŮ= ,sziuD+e"E醖[W&pLvnS? OZ/_`X(.I-!N=w%k1jvv« DL!ksi,Wa #b`YO 7#, AEHv1P,%)_!* 6MzS?u*<)sfEvDF!D,L=n3J#܉_)<"~ zm8F8:# b1ȋU :o*y Cl&FY"x@σqx4\(C ޟ>uӷA;93%=Am+Q0E,I&.YLvHf6tOy6`' k75#y'JK;#wfDߥ9#Cfj 3})p KO$GQvS;hdslX24G#(xb= f oA42cbĤЃc{RC VFYƨsŔ}hʶ֐ ) sÅ+AJ39~{}]WC$XV".IA'Vټ@rHٮ[7J\_nR'$wݱQ{d*|iѣR9QUC&%l<qLl!#Ww#|y;Ysd%x!֯r?pEg$ m7=,d [%"}<~Clpt1:]0@+^ U~^f>'Ikpi8t. -jq?ϚBPv)VrglrMZmVm-+>mŠTz7ɝb5ZHɾpFkyY%z苮w2)K!e[1i\.n{r93=K}Rq|pI7nb p,3B{V.MlxF̢xI,S ybS21s 71H#Чl8'P:q(ذIYUΨ36D8UAZm: }:r d |/~OT}&/vzd!y(Thdo9tya0aFrqrqr(l&r(C%}[KȻcz1sޔ]`X^v2{8ȸ4FHV*^,\K~_C) ?_#JF0k󅥽-<9{ԗIS,>8:Ha\G4ƱxOW@ ^= ۪ҫ;nlP!RSrq=V)YSPl+1Huf+No̠' ud|z\Kim(ߨW:=8<d?+T.סH"Of`^Qyz|RG77`~r6tln^u',Fv(`l 5'je k-Q\""6$&ۤUE٪p*IxPQV jg׾zZ#ԔQ|"v>|yJο$n'ƍ<<^g#@!L2E;4a/Isy2>|X^M|X=Ua[s+bP߽='<|] |cv0$K}//mU, d)R^,XC+ڣw[&%Z_=ʏΪ/vնAG~,zcU!_|J&pc^)ZCgb3uTϟ/sm#ᳶ|Yn?Rj1%w91@u+! 0r€ LK|G5};=xMT[).]߈]yq:S+N 9sY4:x}_HSqUGm;w'1P5omtrwz0$ as=CЋs[B?WX9=ȼ|I+r-;WŪp`tp,\].>IPcfÀxԄQ MfR0!OqXŴdH+!lB&;1 YodVgHCJ$U{&g4 {:V$S9Ism7+^Ԫ {1V;|!y[-tL"#jd!<_˗H0J9X| Y(ΛZbm84ّt`OAUMXͷ:v)=䱯:?~PύOhA*hE߄?!6S?G݀c4Uɠj/\@W֖c!N)]s, -X6EyvY<*^P)n] \ z.7pXb2jWs:vH*՚) QE|QxmPMKQsa!5Jrc7mBڴLW4 :hY @+jI{.s/vUKx ҄DD,%3EͬygaYt"ތg2Ы&6kfTp]/8X>*?xt=bXb'L8nayN; ΀‡100644 iprlib.hikpyCɦս:bM;i;% Q-myRZܓ0Jx;pˆEL F& FFVFVƛ3%Ϝiғ睼pdVoOn*4y*ɯh @&Kqq(AjNqd9Yl~;7WB. xkt7ӆL F&@[Lb9 x{eBgRu _;;540031Q,(JKL`}Z3)kNlxb[nͻ? =S;cg~='3 b9wL:rSwƽ'1.bիr c3.ccsbdO&5C2xx}A/jvu (/  /*P zjDi P} K +`DU o;1CxZ|+n);] h xk8r$ˆEL F& FVFV3mݶyΌlTTxkd7ӆ#L F& FVFVo1elj Ix{ED71;5-3'AM]_u׍*L x{ED71;5-3'qbSٕ[&,l%Uj# .ӻj oPEyiz 6lXt?dJw%lOr'MSg%SEY}eן<='3 w_ {m[f,w W(l%*]/hЛU!1Ĩ^qD&»3|xճ*J)_Ī:jV^N^Cߕr&㟚щxYRzm{2&0 h um4+HѰ(cy5voBY鬠t~prY 痐N xa" IF KM~63?b)gzg ,ۃ ʰqA691×#r8vŰRdONmJ n[%?VwO̱n)LBM->[ŠݞF!(@0(nu'WHl4)N@8^RN"gP^M.XJ[o?[O Ա3!io>7(t>xq.xEfrVqx9UI]#85mv*)} XXv. -_:xΛccB0ک$'(ԴEְ6V3EtLz[ *\`mϨs{0$I:vRNIA: IV0ezVrqMLu3cI1lT7 I+Sdt0r<2\Zϕ<9qX5~d>ˈyg̱\H\N@:M*<N6!Rp ZhҴ d]ga Fr>O9A-G}˒ؿ ᖠ_(.|X{.XW :W9Fqʼn粪w3WAvsi2e@mCA&ZvKm:%-Smu+mč^o Ǚ\_  if oܷ6{Ё;\9L2+ia^Y 0bi=/WPM8НOg2Â7FF:^1`, tT{ W'-;pujšԮTc݄$>5Qe z>z5zH LtX T|7^I͸Q{t%Y((v:ي)o1)7©O>MՋA{j"۝dbWX 2MO^~pi/\ |N/ zcq&Eg`f@%Jfo§Qdm@Vc"(ˣWFݣ\+4> OI~1o5 B Z0Q+[=~?, 75h`#wb miMZxkj`.;!UVr}USE}{rrKEeE.e=})MK*3IIer~XhN6;`P;}4p)rl}ldeÅDћOGPj1fZ|>RnϾu}?zSMu~ΒM/|*gP7Tck85#z,8\ 86XyVb )U>| y$L3|$x?Txg&eɁC&RHHp)|4뒬}kyp좫|0~eKp蜨ӊ 'g3 2L%.c%ϋ!Zds"a9LjqN?Nhdis3YO:ߏY:%inhWTs!u&i3s (+0ޞ˲؎y @ĺFRc6G@?ܞqz".jAu,eAkh2Bk93/w,FP EjFH XT*@pgMC޴)-KH=l I庲Jazl5[&s QbUMݿ>u9j*k5U֘F-10-͛XGK uZKYMm9.x߭t{cIt-nP{yM,Q-]ʰu:E[#]uömOXoUS䩕[Y\ߔ%ߙZ%wHJv̴i0MCwu38nEn?rk2 ♦Ǜ ?ӠfuzBl Lݜ"֙VT5,pn!9|fCT+, ď=<=ϟ{2?55GV1l)z:|ǣ_ bKo~AE+PY*y_'Zy#ԩ`{&ζq+!.5EH` Rӑ_T(h6b~̻܋F a4}zVƵ|砗wlDWSV_ezZ*Fck8{ϧħŠո]wZ=@ؚ~%j~k/fQSo75!"uְxT9R]gQŐv=i( ,dGGCyED$(E= <M߃]dԶ:[@pȀyKE={D{?rǹ`Ͼ:s!8Ww0C5vzVN 'IF3.CGpr.>ғ,TYQ#&q&i? jH?WP81 Bsy#'ƮX\j]JSO\$CNk!a*BN!cW ^1$sA .ZP^<[f^nc2fU$O)3s|ԀAEz^?т3RWV[iu-:Dʖk:?=cmw&)?=} <.qױݛbخgC;(CQLcr!<"PM UY8A2Vx"iNzTA|&~"ԫ_NDC} dU'U2.'GD]kd[\L1Y%QXLhj1Aw i?\ e#𿔔^PQa]}|fK Q,u|lCj 5#: gƦNl"԰-%]KDŽlghXk'>N6W5iIʅ,%g(zNON[/<E@n'.T@\MAKjMuVs-< H s$Pxh51om2572D% -I!UDG#]R5,"i ҩoq۟||DM@|v?|bMmyo;q$@g/>H WKhn*nR' I }l@lqAe q 5#i I"I-mr0p|#uK$B:B7Y8%HMdʒt72\, E}rDg6~$hxvAc,QL!+KDcSj47MgZq])JR)ײi^-!S H8Ne):(%>u%,L5 ij7VcaؙO3pq%'^%'^% >0ʼn}面cѨtfeէTu>SUV4WUcN][ݰ>U^xY0"=Jq7qƮ-ANȄI cC|*ro(_v}X1Fy;a  An|99ڲ_ޤwo3SϽ,sW7![ۃY\黀YgNa?)fOs٭Nf_PYv;7avMܴj.BXAnAduRֹ=,gѸ,NLܞ\NRFp~baU^kHr }O׿TؙM";M\s,\\%Y,`˹KWUa33t$uM+pA ~"- eZ$l._r/eeJe}#;5vZ`+,5^' /7/EY]e1zkM|) !Az_}a;2Q@i9mVe{}욃lz0I->*@R(#˩V^";K5 tj>ْ04]KTffua'].88~ӳr߾WP@HDz{IK>x.| 7áQ"uUw8SѴW͜ IpS]}B>,Bn.0}M4u*N409? m& TWʦe3_;b@Ȣ@:d9aٮ G+`#d/ ςim&O >.4:bT.TbD,AE;q/|LS.lggѦCNP!|fO°$͝Vy 8ye3GG Cq[ )G-"dj&P(RТh"͟#NK3d4#`؎XoD(C97  }Ѹ|Jb3y,X,Yt!*+ҿ$ VeR=Hn/2H|IVdT7d!C"/!H)~,m~^AR\T ۗa|%mF;1%礹A_-_.i CAwgkI=vA1"ϑtxyNJmDoК1bO8΃MpD(n/):j*L=aC ! 2Av[C4#!7hNҕPZLFbl`߈z#VK6ʟP$o$>4ȕ c{d%d.=uaNC%WzXߛHRQވ%zE-0-^O 9ԧf2 =,)_NQJT8wKZ\m+ur*)^"DrN#6D:C{#hqSGI:pMO]H+2I,(ߊbm@7(vz;ۿxm v[7S?W>)Q?V'VWf6=OĹlJ^AN۹ }3NSlo&QF*eP o[6[!t)Pj\V*^.PA''#cKzb/7A;mq'״"ҡ|%yJkwn2NWnׇ9BfR~N^ID6 8rŅJN\0YVCèK+l;w 5 31Hy0e%j2?}}XMVrB\ cr IkU,R@3R`Kt\q`݂ A'1OO@9Rzf |asN CݝmhXLξY!ɮX*,@nq}M(?e+|̩v8mz񣰏,xIsu-rUMbܲOdי'"W[*W¶}$}}қ"󀣟 xB#xd{jGpóhe )1| l!Y2QG0:W/#D,0lhq<{o*-'H&;A$j֕'66P +O\_Bb PЅ=z@K qE)@p !uLza!/7$s!<}qah_aD'Gfq,tq%ֿ@-TF{"(Td*pW}(B=S  z"RϬ|I/!9 _"al9KQ'/%^"_^RB_Ƕ ` GO 9\pi)n< ?ihԛk=.đpU&w hƝ&l*<}d)ֿ\tU%*mOdTt0M2#%zg U$ّbmD4j^RQE@$vFIpHiǽŵk%`λHCbL6n,S]qܨt YRRo!J;,}F-ve/X|V52?2 Zo4<-Rm 0(CZd s|IEm8,YF3ub0܎&mR R+=XϞۤ8`ȇxt#QBkv&'Jg#+WVSWeCJ_e U2WHbEn1j+@{!)+}k-檪2;ª,4mt|ӟ i_>J>rni "`AZ(SݵP(1BIC s h"*苔֖)~ۭ)QɥJL^ KΥAѾ?pii\ f);9 yݯpt f b`0::na6MR81O2_ F?IACX1oއ3TQ G (9B6ji#@ hgrf#{Q/Gx0\D%QD#u>h ٨R|( A᫔ˈ) =0 kT Zrbl٬f0jJhXP3GeGж>25 {=1k^[{Aǭ#~ƚJФѫeP:j%.N BzԞno-ú2*5r3` :g LܒI )S{3i2p/SM3r.o&lSOwM?I}&V4Xjm^*İ"zZ,܌x5ʮ}"_Ǻ`.usx 1n[@  Cx۴ԣ<&~L+ed02fEA=P7gsuy+ҲIIf=Zh}ͥ#|A3;Up:Eb}6ݾ^H@Ei4 Y%4V%=YHr/C8.syy,+!IBDDsٽ _#_f(Axq JqX'Co;)$+* A<$NG8DȀ1NArV&@%/2=0wwQd҅ h*90; ^<N#m'!oVOTGB4P#DdLc J_,Tfk!NF}p!S63S.=#MM?ػbA.%0_qtÙ8ԃd yS6l:Pm@;Wv-6SD7 åQOhF= Px%jA7=#x\g;&+M%|ΚΪ!,:܊TllMQU=<^F\Co;,^C$` lkr0>TyiO0@!Xg$ ߮XKu7J-T!5EUd2<Є"d4Jj/Ĉ$`6b3-.fq:|8S.$Md4ED]M8$}2hG/"~ D 5}EC&X)l0'gog8["e۫סviRȱ2%EÓB3证(l n +Z{VR% -jinKKhJȱ\Ou8BV)pN3hbJl?D!qh.x[Ί c6LWφlfC6K&yKj꘱#YQ%ev$9 g^]> ڽb(h4^@zɲ-2| )~z=w”S'U r7z{{Gn'yyN#7W)R'>n>] "J1Ԯjʖff.[=- ) %*kwQ@VQ~9*:j1߂\s@0'*8u۩7.*YFغ(;P0B](wT2hKP/w?";ypf~oC)*QӢ/l=W@1 xuba:nkċg1?>wʉj|Q|;>Aߑ.ߩ4lC=ٹab#|8H~INw[AZJӫ8OxBLi^+Ru' Xt>rnq 'zoI 44Zj}۝Ґ>ZVk;vs)ik|$;t `W4=)!,h_Gugky\P) j !0 ko|֛~8J!Ac%铐MEwcOBFHmֵ}~RiK'MSB#!eEP;?isfMT@X#q'D~BeIަ(%&D8ST+ơ4:zM5͸jy{;Ղ/5"@/@m<nH(4},X -p9a/d(AhqhGw K|z\ }y+>P% <9Sm^V-Le5ѠAc_"N+#jxk?"(Am$k[ }QB][s[z+Dy_4gW+"\7Ue\ѬɆ^VVS]L K22J{ݲ! ~mnJ!gܠt@p۱_  #mCýmu$IT _?*-z^bfY%@JXznsd8pAM"qYg;_́b W_f 6.._'b E@XM_Hzг3G.q~sbZ#xC>2q($FrR(Z$ib}ʬM}DŻPc/KݛUk:ſӅlxgNj2Q zxȌ$s4[(gg{25e*sVKQVX}' 11AlD㍙D 6O ^a#j߄< _faMH'/C(U!,c}Jh8=ȪbnRnϰQŭFUG(=yGխTu*+ʛ P@nZKd.7[m"{8S=Rvn7qқ ?%͟,؎\КQO͖9ZmC lL0!g j9l<$gk_%}/́D eHZgNec*9mjuQ|i_G\c\6%1LFT;TGֈӴV1=ل֑ נ>o,0cZh:9ԀJl5$x3MM $n &S-1LRnkxyLqͫ'dd1cxFQ0E#lĹvͺr$l׋CT=yrv?$T@Kk-4>7{ݵ=EBa7A [f"^RʷډΧ{DoE- />.):i*q XQl؈w7ﻻ ^. )p$24rOa9;;'? őPWĆ~i)p_ mFhp؇8|b(;[~q#C3n wYw2 J@opX* u?ՒM|\DݔPX1xWnޑJ+;D@/4=$'A ْ8oFuބsZ<8X %TrJ*8z?ʲT*q]O9> P:#N wQWJ(!y '+xefFÚd^+= |::&(w{dpGZ-N:*Ν@Jcl%Ic9ށCRډ8&Ws@=BʎA༯|%#dIq[!r?i[/$io8zJREymJKbqRBg8B6eN*"yk \}zkB @*(ZSTPQG<kI~&D AޓXj?^k BrMbI<pnt?Yed[by{A"VV[V \7&zмɨ&kZi{ZȺVqk.y3x>wÒDIrq0$~9Q]~*{E!  %k;ߜ-!"YZq `f}T:9NӆXy0N$KеSiqDi8Xw%K\yFZ[S }SCӲrX}lu5 "JoӤJqMnk |bZ3~v.07O{YfM;xr8&/:iݣ$=6]=xCކf8[ۉmG-ɅUyw4lR{.,_i.y\e2r+!/YSWYfRjƲ{tC4 $ Rd]A"zݹVd\$nu%Iy]00p~`1DZOe{ qS+8b?qʺs$+ =PYIe΀h0RЊå6 dn(ڄ+ȁ.qw]3+q).6Loz3%^uPL 6kZK)ΡH2)&5^D! ` JsU:',#gp4. wJ#3I7.Tܭ*JbmzҌ俤f\_#[PJ.1Ş? \X>Zw#,֩벙RmW-;fIۦ̖F?]*Xů $.r|d0a fÉ-hɀP ' V}yJmsd~ha9#ixH%=; <0IUȏ#k!)'=jr5"r>_a:E_et,DxXkTSW^PB  @y+B:Ac$AS$Ullvjm->Iw*vVh.;j[m(Qk:Tt9& ?#}g߳9l{oKtcu9ٹETNqEp,@`) IDEc&Qo;f*jybhwQnv]hpbAѵlakJ\ob.</fM.f~3d,wOSIu#H v.K<Y=SO=BA\(D&߇Gd8%Q;G>/ &d $ִǣ(y"˹.6F< JXB\(]-o(ڎ}#z;ő8!ak?@L\5b[Xv|>/·>-3 V7[I%\Qx[ȮK+;{:Ս0ПVx ôCZ1}F#1J-z[ ϜBg9(wE94UI(( .D=Y烙 ޝ~ GK &Ckrd.8D2g5J2aޥluđ}5sR Whb@]&w#1y.fuY3=c4XE<ָy"ăr箄xΗ5HoURw5HT >#lPظ$ ?D-n& ":\_=^ۯ/Was;a&07߯`-^KD_ȕp@b`@nXK-AUAT.K"P8~,HsiB\ye9|[F:̗@$P~6 6 pH 3osINPَ`h[#p=K WNJ"_{c:\/ `S*:I!y-/y- p3Iw]-t$/ ^7|Mc9x 2l5b.W(z^)F C>"Fk0/A\]r&J.{%h'^LCU/WebV%t]?-rT- +Us)GD~0< OUp]O4Ia ܉DjaF/>)<#|p8~NE(g=C*k7 Ftbzw` >➐?)Q Y,]IÅm|Ǣþ[аD~<i ("x{ED71;5-3'a畕 ʼ}Ҏ;D9/:Qś e: x(&1;tbY?]UKT( sx{eBk텶6R?"¿BfAQr~^Zf^|BS 3w2>y'ީk|Kyq,J߂5B'#d&H\Y/-ѣYϝ.><1A$ ukdε9I_$uSu'*SWm^}E=J>xkj`[$nj S&WHU-T-RMS(#[j*CiU;xOP3( @4B̥ã 0 фct:m񚙶[h|Dggĥ C213Zj4qJBmyLtqz=w?V,KD,Rbq%ޅ]Dg&.-{iƖC1UU=uX1Bg@lسE5){ϴ1F/XmCސa!S,ɒ})GǩTtO*LLב&_xIj;Ŭb>DnAZJ`Ó B_6(p;`Пͺ Оf5Oƒ@U[6gScY/$;N%p%2F7F+8ʬ*' WZ/3<9¢w,S?4p@hK1vEQ %uՓ컵X 4#6VDeĠJ fbض}0vci60V44@mdLE[CGgl,KZczK=B0'qvqN~H\eK$v1ш1M&X$H-vD,OҔ A*Vy$Ζ~>XaHƨEJ~'7lS&M>A?){+׾ |~&q=y[*}z i>;:k?o47OHEE󔬠PΘwk=TqA4s'K3M-ӂXNẒ,$>}(CZϳGaIi$;AlT@ q۵f L0.4Y 4rMeG\ɴKG40+ɚ 2kt-m&;RȎdݤ+d5eh鴙v( Qҙ坏g ;,'cXYুЮѱ.No\cQm tY: :7_NoVX:- vhxӇX7A3so)Z&bMpmR vh0p"69@n@Ơz;HzRS~`i Fo \rznaM\NGc=S'3KD/ L8X!V?Mwq0$f 9^Ҫc69S$(T5b?x[gX 9S'w{5.#zKb|]u﬇˜V3V@υ;z}J>6oh@KbzF!. GPiݙd# )`uJyj{^O_)_$T+[ ΨkC;OYHq&Ir#Q6֚չ7rfD2%?[IX? ~y? +ʙwBY^x ?$JOYu*vAv$1,򷿑l c!`B8w83| (W]C!sㆇ$o?fl~ "< Kɷd'[YǹvT#-nTΏ׶B;r>: -a)a VC$ZQ-bYmE@/<ܫp>1`MW8/;L^S-9`_2ft=wfxPIwb!ڄ"J[`h{]ϰX Y~aW*j46rQYxV_zڥʴazuhk-(}Qq4oј16_Ų 'yqPuv/dVq68V:NX]Y GmmȳY[<>d[$҉Wy^jNPh4.SX6#0a05*?mq >u9 ;S77M`ݵ.UlbT Jzow Xmt`zw1L,'zun·14w2th=NzSG-+'$ґq點7nidztg81>"|'k︞A)I6"*Oߙ\YyKmXAth4/\wz $S?: >;-}xr&&C҅$B=cr~ ޾|ovq"J>u{ׯ/Tgo ,Fr,&Cyx*CxKT8!,ה(I׽dRu]XE.EASK՜4_4_Vt*uR u|yeIVy| 򢅷A\/YRMa,GL3΃ B`'ɋIr?7 [P`ɋvI~XrWIHvmк-j G&f׏ezb8 {' V~gB֠`B7X kՓ\A` >RLf=T$DD=*լ1jU臓UߧN^0 !aMiy>o#E|@TZɨ㲭R=b٘F,•*E]Qq_,2yMPOG8Nh)%F5s)g.Y4-6/[L[g߂Gd5]y^s?{ 06n$ ]Hno'v] BLҕք@/zy^0V|+ JX[ i}WizG֋* /(솹nT v^70iE'}' ~5_Vo9"*zC칩g ,ha̖mL'[u.n}*B8A?N3x5fv_W4ntAay$N`D''%-^p4Z -4 P&0c 4A.<ҡ7Dx' a5)T3ذ%.uO Rn`V>(?Kaf/GQȤ>P;kɣlq ^!(,&~rvJ:kxuSqU U>Bj+ tjzrvL#ْMUa$sf fBO0,|KW'I0|Z2bn\jk[zR "X\!wC ..hXs7~Tgʄ2Z|+$Wi/׏vpFRcTaCݬ2(2(ˑ@~iXۦ< 5e9$REEHr\؅AQ[T7SEq,1ܳ 9:)#I¼f L9GXݫrPS,;&-+zS̀`M^O GMBՀf7nobm hP&-܆nOQxܙ`$@ ݔ8.#mQu(>#63"jRr `PHR2e1!nc^kUR8G>N%=Qh*);4T};Żk"& X|\J]O h#grJ*TQՁ0XM}A Uu, UկTڈTs *`Ob}a=8|ߙN@U8T}Ҭ5$L wUS>12tbpCM !TPp?XF&6PlF'33wC}5XWM36tXMH* y&gl;JǷáޓwpɓgԷbF' B{9v{c也S!y"˴=r| O;IS`vf<[\ض"koQpDi; heAy*?=|Wi uVQK8#.4[v̗ ~JTNAGrof̏ '&nFC8WW,[B52v؞xJG4z" uHpײrR,#O%*Cw_/T:٩!~@_  aqC?ГM2a*[wyG/䖜,CeOė?mO;ְc $yl" =$u C /2SE˓67.yHWnbĶ Fۅ =\+5.NLM8Q7 eR Lb` z÷Bjk>z]n5Wl ڵqJVSݮN,5I15'LxB\e(H{EdO.U_\C~CANv} a\b|՚}}#Up!m/h۲kkQ~Y7t<]dVFS,WMe~:%" ? *g~ܜ/G_:0B\s:6bǿk7Me]Fܠ`B2SY㇟ɗgd2}=q/3/Z80HφNg&^g(ib-ʘtiIT6ȨLŽ\ X+mYdjs+'0W{O$ɲ55Ipt:ͦ\I]}BN$QFX_ V:=5.>٘~VqK@7&3Yadu?;bMv,Vcj8SӘL:.7EC2W¦I~.tedYK#L _qp[PS<Av_wn)R'R}3 .7gP(vI&!ŏ͊AZF7>XnZo_tiɣYcJUD@XBJiqK0ޔlqB }#nFGT6@*_2Q7hmV)ekYτX!]xK?hQRobJBOI8*sv+?4?SXxrY-< Kp2]50&O 0p roԑ^=>l@μ<TtTG]dv~N? qu҇<(!('5LJmxl$rS'#f0yHIz-:à`axS#PYRod4&ŏ%5]cD·oG/2 vOƣסWkS0V7ع.nVWJ~w_\->oT;_A'Xgx,]|)H؄!6]nuYt˙^hsH'_Ŀw+TL[U3{$?Ly!p'#t}~w'`\rҦ $g t.ĩo B$OkD?,?rn}?"$uNU4[S( GɌ9ZH|ǜnzSfىF pD <H!Y̬oKp =q“ޘ!zo| 8$ #6H}˼*/3, ]] $.Crb1zSqL{_B0>ſAxFfP$iN^7@ &uiMN_OGl?h@ נ+ńUHJ\%V[vb.h+dй(܆Y9xzTFKT%|luԪ=xƹdfnfKӴgfYJ[QPG\MbȘFHytO_єEQqȖ_.↍փT 8Go5 0.| 'cBDǛw(YiK^71iLQ[{J0P"*z}G+OP'Čm"SOo 1-;_o`Nq69P˂ 3)hCZRJIS"VH6h]eAM&ؿ1h^ @d)]K(W!yϔ@$hUҷWqDםLUEޔ}o? CQcd`hkm( *1k 0\ C߫с *RG_;abj1..=;H]1sB2ax,ȂJլ!a D+/6XIG.vș5{HZaeZ5hYbR*^pVm `(O >B+cu}zK^%( Q~>㥱7moO,ZGB*@GeU:"j@nUG|i m|7'?7AwAH̵7x(.\971I,~k|E._4XTXލ} tIyb@>$d>0iL/H `)>,&>|¢Ш̅s!}yik^3@Aq>`Aeu}wb_^N҉2A6;YTazfUyԀu^}-:V5믥Zn'dǪu!=]BݹߌpGsgI4_; x4+"$L+a㵼Wψ ">d)ȷ˟_R5\4P>"ߑFeKSt}\}yc9qf 7$x4i*nn~JjFaͅ  }"x340031QMNMIejǓK6m!DM+ói;#9-fw]q%'nR P,Kaxj~fqsͨ2Fd%eY0p*ljxlY /سWa(KfT|WVgM~]y@,% 欲{/-%O^64h,ycc0u4E ˄h=\zIG"d&\Yay/BDPd0jx;*%}cfl~(19h*U jy`*J RKR.gzmRsMCR5Pvl g_?LdƯ|^"Rsogf3\jMдk:GmlڼHxk\':qD&UT̂tә'M~d=Y9itHa.. R+c2'$\wNf^LN/yr$KXX &y8L0jd&U*+V$甦*8tdOgXYġ1h48FhNo$rɛ9q/1+.J+*U8 VSx['FtB.nd\=.E6x}w|g{;INzH= kB@ P@)JP!d{pWPb ]ߙS-?سSyg&^eԍ憖||_Ruٗ؜lGNVVَ̮}rqL=gM/! 7Zo‚tZōV8:Ww`=пSX frpsrJ w_jW8}0ŗW\n3ǻ }p*? J.^X94qI8?B~7 iLzPjיw^|+hKژZ9} 06͊Ɔ:+ͪp2yw'6H>6! zy`224څ`& D^J ?tRd2_JwrVDa2I%׼!R,-Θ.R>,"M{H6GX -9\i!3fPεd-ğ©W4p|DCK7f3ÓVWL7eꢹ)ӧQ?ηK8N *MCz/v>҅ &[>G3jt #_y+lH@w|(Yyx#Vi*IM%=BAKy3^G|R$A@"CB9JJN`+xptShsM_pfyMVYhk p,(G *WgwR4 L>dxp3_w"s܄`;AIL -#4@k\0+NNh7L&(bpwp;o;#=6t{!Oo1kِPefZk(Y R쎘*RCDpf;dc'5 lW^dKA>> Rd6 &ca?y;{al6( ~DŽcx j ز>T;Fe֏~ؤb*K9ʮEuְ; -n!Xjw2Hu7I(4n37C1Xga)wlw s(w} 3X:f4h0-Mn:0bYSC~syG]>7xA\0)>p|/Pob K5r(C h߸ja9.\Kp?45L6G |jg3HRA w}BO>k|n]a3Ce.^d[%!f5(pQ% ;;f h{T_acK)JM[V\,ﴰy60Zw@<]$7e VhWFٶ;6ZO -M.@?~[o8)+`?B5錵Dv(6-e3 Jmd݁c[L3;N=#]pB(;<}%;*-p7۞Fa߰#ghI?LXq cQdȻĆ/f N,S^]>[YWK`gi֛]ޜw4o\-n`Ý:19^K!~g\HdOszKx*m c]eC]ٲpgvsځ#\^u^ҝZ=kH/CM4iTe+p> E<ˠ?Vv?zU) om,P6Z.LZZf7 r}4eʚAZZp3xI=,tBώ07wg> Rwrr?Fm+XC^mR? }?"]^㱿,AMӻ|:UXCć{9߀0m'[9FXC>zPAJgIhh9[9KA'̆`5#(Nw c9\,嶒rX|0ߜ>$J]ŕ0f5T|W5rwBnzdг, ^[UCF.2Οs]p'Qzn"%;=U*Dob#OܫvpMd32wpRXXi'XU*3 幤i~nyж+j-8NR 5_ȈNj5Py!lx9]owXuW2`jS n?ǣTƩa ekZZ˿X*$U(R8}B'5<2YŐiSx, ?2 0,%f!W~GڰYZ_.>0f?f"@{?B1 ~hpUPV#ViLj Z0"paf0|cQlKߊr71_|%{ZvSL_a7<|7WDf5ӔR%zkB#ZÇ苾Y ǟzoYj3u:b̶ IIɌhB!b: Ů|_^8y\L`pW滈,#+ȇme}JLe cJmP\~ft >| bN8ȿtU2Q!M'd{#6 $ So C( 'y{;jfJ*ĜbT._SdΧ:4b-5Bg|<8Y1c\a=՟6 l| W~p16D1p+{1ڶ'qCn&ҾAȹOd\ [0Khnt!l<4lώPذZ\Y'ز~*ss =n:!Ò1b'x&FT`x7o 3_(d#dBW0gˆ;f7 }ݕnDba rN;g Xf[?>tLPa MBl\FLL@mÅ,l㒌c YZ J_M#FXajde MxiL*]5'$V*!pKu))HJ}Pm%9^\.ܟ?8wt‘[]XIhT˺\ԼEH,sHo,m$QnXх@Uv4Mfθ%عbV?)vY{ `fX^к»ȪG?Um)rzuPʹCZy"lq7Nɾ8H=J8u8GqaBv.pLWEh{N/c% ߕ0OY\1&.\  @r#/pE-x,} +B7WqT1l0\$unQz;Wէ;)Ag- Q/+0'L3ǓYX^[V>N\R=ׅ{Z1t[Bs_X&}K |gf<5 mr$T$B% ![, d;g4=D46>bbpNAnݨ) ,>ztI0jQUH(6k GBGUyYd?cQ gͭHGx^h89#N>?0]w!C")dɵˀ{6~zpTpi#O=+3Dx 1a-gXyb"x LF2!}G3 y $N*rGm5>X\'}+8 j{`M[Kc8k FLNAI8ggEm'{>>KWT봶ክ{q)$J^;ӻbhpʆ a?/T#*9ݿxO*cƻ|TmY9"Be T |] ~¶IlJXE04; |K 8#{%1#\)4r}K⡷U$^Xѵ_#@h &RJ(0c$)zz{YQ}!+N_EOqeċ|OVW[|8C6m@в]$RhN"PLXeXwfRGA^&{HgO58X<%9#h(+<W Sgh)jJkfxNq_I"ۊ ;HS#֕*Eg\r~`}4"b sT\qXm/. QP SIkTρDZ`}͆XS芩5b Weᙿ+=@yY(~|EbyY$vV,R9 6(qBr}έp l@~1 j/ yNr,y Ci_7pA\hp8rA||ZݱE#nG_ MZDJ1(x_ڟa_ⳤ=i5"u*HW>5 w*t'?cS 57:!6&Һv&^"IWiuOa l1˅T4/"53ERopؤ\uDl#S۹`R]#=s@0Roֳk%K"rBN?h 9^/}RFü^64v+cT%'y3CH<$٤0,`+GCk$YlRI~d $Y[Hq$76t?H3@I첟f@|A✽oȫz)yX# ,{l'li6]x9 ۠5@# 1NɠAR] 1%ٛ|< Czq?*Cd\k~zˀn[# N49R \ zH w8mG+Tn! ʭPH^&C[zSe[KCMä0?2R& K/|n?Jrz]su)FX~]!@Ҫ(j&-SVlbBˤЪꌊ꾎ysͪvd.WAyvUB$#v4 nC tDH7 rThPL2BΖy{P2]9N NK176mNE}ÌKOK^)[o& Bx^WA1OQF)ҀQ&i#iOAVi ǭzs|T"AJFnucgͱwFkMީ v w-}ѬO#ۏS+]:b.H[HgbݸP^wKӯMR<(1 PB+U;4sҍx4?\ƬNx?yFUwPR=їKsSLڹ$q+B?ᡏK?J%)/:7Ǒ9#qca4G~{O?1@/F<ދUpOvoüeM**ta<ԙ؉Oo :N8$nSbs3}Dݼ&Cf͵"G w>8d!|9>L+9t'V[͆9Z`X}|G>SUu&4}S9+|w4x*UIFF{3E-WbR/]c}YP@dZ3̓Ùrc%4EQ`ѓ$95~0G.qo$ F 3J;3}l<ȋ_>ޞ-cKcЈъ{''*{|^'r?'[W_{z9i 5~pO缵p<=͚(gk@?M 5઩!>$_2<Źc6(^-|to~yxC5Q^UceS>w}鷛otòrP"yG6]y{K#Ut\.‡4D巯Q v-T}je6ReYh2OA^@5Sq[-t2=q %34wu:?CV$3Yu +YlG  P k?3D$]*F˔Hq;9쨳 L>c}JulIiIW^z*SVzk[仯6Iޣh٭XauXMMOb5Sv!نN&'3Qhr@MTtݵ&2]Dk 7LZ'D?{j*<4rype1M|0l~G臋,Ev<UN1z vhٝz3 E3VANk9 hș[ Yi+I!0(d!wkͤzKnRy]{nR2LW7)͝K.QSi-]l*}=V٬$wi YCoVqg8g uShuX G:ruW~|s#V;k''PBYtr^(xPexJ:y2fc< Eq Y C(x@z,!<Л)mܡG#;ɃYrPY{횓uSNgc~^Qj o(٤0nKU5I@1Ĭ4':*HSE!b22&Ɯ?AY,=-VAA%享<a{qcJKhtGqa%Hm }X9x"f  \O(!d|Q̮9𱃍G ZM:ˤ!`BY^xOMiuy=r\ֹҪ&]>rc2Z'K"i9de&F­U૪pS45tLָQw<j?wA!L¡ o)QjS 0tWU/R{G kFh 'zBU=k;dAqFlu$]rЋs6oYn83\]8TeF%~ԣJΨ+7Usќ8nc'Ec?0ݨnL*μLsaMe=&}YK cv;d$t=2e߬ʂpt:y4etzN \>- RWȟKS #@F ?J9 i>61GޥnlꌼG @vu˺(/TO^ UU>bLK52p9 RӒgrWbqԱjoczT ~(Fw5q{!.{xVzd Vj'XGV1wQCRً' "ܫvNHW X!SsRP ?'%:ž}j0_5{]ƯC>u3µn:*+ P}ztV+U~דL?OF޾ :{R0kǷwi.c 5 %k>5R0I'[U?Dυ? >Q|3N-;W9F _Ǭ{.sjcDfx/{)C?D/՞^ϿT2<.π_FA*ֆ.}vYC ڑcCփn33V7mL,용Y#ݾĐ'13nR+J ew퀷<=] @Q5?_=a _iǍ_ [SD6DK%v|jFGϸI#]& ܼJ߻WiKQ۵ǘ?^^"Xxn Z!kkGzme Բ_0m=w%ѐĄMZ7_6 DM1FlV%[4 oEtԀVCGOkQ׫[va缭tsrF/k#^ Zp; 3Mf,aiX=J/ݣٍX߰]imȩ|ndvqks;8G;wg"&Y##c}C}#cC+S+S̼tVh&(dg''g敤%(d'(PlhAU((gVi5RSjDM^RiZZja$ I*(w(ʂT%$EU U ݡt&ަ骃ii= 3dbnbqAEZ:Aii}502@4E)y%E%@S P J# A.N: ^UɎoxA}xWoDF[lh I$>}m"n@iҦ +㬛]{k{Ӥ!298T !ġT!qV'!@̌^{כM%^~w)ه]V2̀4fRJdUE]zX*QT\ 8SSÔ)Ϟa S0[w+A%x[V%(e0hm^^ٌWNQT<]%A1U~VH$hߛ64B\bEX| IpZqሞ,IU4rU@_yg$S:jPGWáPY:띡g_ =ym/lفI" 5LS%OZ MRdEM|4?4Wl5mO<fA'VPmbabId<9|sCpb=RZrlje:Ł3@~V %ED+39~Q_d ݃yޗ8-zxfR$g) J4h_XA5ģÆ(g-BPdyZ7(ޯ-#RL ?xI5&7p*C/>:!l{e QPqp £i/1 H`g7r$n.@;'㺘Hxz-n$R)"zy98A'$?~rh) 4V54Kš1`fiM,5j{ޮCGLW] }']Gou%2pyODw<(C1FH*\gm!;+xq7fAk e'^Uo)0ͮg+,m}y+ACK|>/|'^Λ8 | _s3:9h@Z;F(`rgnfN%:rوT5ʔ{( 5E6P3LQʖ ($Q0Ta"}+S 0ҥ3rlLȩ#]xT$-r su\2.&uT҄D2@#2滏LLEz,hU-'A-KO(RN-i2Q+o[XNMd&ZI Rd!] ,SDT&8,WMǭ _ešQr)>Ҕ,C:3KLhNmwUݯѵr_v.Iw%Y"3N#DrR^3qT'1?ey2Ld`J%2M o ˵+i:lȸrPxӎ㛢 8L,.x ]݃p%)lj١ZR0$GpT3A<vぃNR$|?Լ%tN _톻w%_.PQ%TsA >KvO!N7`HqlZgssE9U#\fɐMdhEDYbѓ>(<&}^&ge0kLԳ*R ?+e߶\RQ'HVE!̜jMQTGk7/g=PN[7n{`~Un%'[x}PMKPŀ3܎ nl?Z,.$b@!iE)Ln6-/iU\t`vҍL-w=z= ?编@S=YUev4%+7e fgVqC2ouPjNNjrj07RVٚ_j.AF!DHfA7Q;# ~}4sfU,Ml1 $eE"\!oe6m#o,U9/;tݪ$/b.IqNhl\ $GX-HXZN>_.J!s15~ϧISwKϭyzAKGc2L: q^x$\|Bg@< 9Qb̂T2} `E& 9ͫ1)+ZX($eegnhr;kz j$fG'*mMI fB.UԼt %D8A ;x9u "0K@=%9i): # 6/2dU*(Nxr ˘$   ̭ &bZ?y^,: Eiц&3+A~L&=87pbaȻy %+*eg'Ƨe'g&j'$tJSR9\\0ݙi Eɓ7ii(嗀 Sҡ_䫠j`7Y0Vv:]n̼ΓKٲ* )C<$CAx2(7+6qB< 9L֓&p )n'lJbq oSR2Sai̴ĒԲԜɁr #m<]]C]\CC\&otИ6Id~'y6O>0yC $63=\4' Mg\`3Zp'E,"Q G0+('Gi+0-߁`% Ct`ED@xsem~ Ӑx340031Q,(*-)+.HMf1:ދj۾>E_]8 pxqsB(1WPjNjbqD/"w⪻p6L\y9\Z ! ~e F F NEy ޙy 6IE@ڡX/3)W/9?NP@X׈KW-"5E85=75$$3?O!-4D!3O! %$ h5xmA 0E4JjYb$". )=kj]èj <25g!pY[c\Pr!N=jƚN~2إ8^g?Symt[v7MTʉ/i5;;<ZNKnt1%[k zzgn8IQYfJ0akin]W GH:}"7B rpYU?_!V ZD_8,eA~o:α![cOeLE߯LI&iW_BSq&oA$PGW UbAu}q\I=a)!?k81Bau{aJ>D/Qeh=hj[86ί+ ڃE9(2iJ-*Ή^p6K20YCc3k&C/2--(8N-M+jt LSѢp<9ٷe7HakIqZǶ}Ͻɋc._8R*+ji 51'nqY .`qy3,. !7̯hA _IR"Ui1跚ٯi^Jcn٘o1EprSJ3n~a pc.U'RcHl‚16ѥ5jz80qcrhΕ-y*t7RDҕ:zLhc]:ˠ75k0lEWrs 15&k3cw&%W 6|Vy:gN5>;6Dܮ17f2"f֧"VNn ©gw:]ioN}fA9HkCˀb*,ضv"_2J- a<\10HL5K21.l̡Í)t_cj 0&wawrnW﹢qľ169Y( Т3ۚ=1MloF7Sksl6Б*:2OK[н] AP`<dy-,ua/|Ғp뷣}aY+ ~ia}cWqe-_!#qG|A342o8Ko̦wZዢ.CӖB4PR+]0`>= (j)4l޾N?D/9ԓ_2-Z|\)ap(YqRC#N²pPI!td+MwFL(=uOSfs~b]h9Xe2*#uOVEQʹ=6/ _[pZ~_k"H(MOUY^G[FҨ5:b֦F̨̚D:Qtm'л֬- -+vtrDօx,-XWde 3C\B oK2 hg}!7KLϥ7*q7=Yt8TI,I&t/+ȇH)ѽTcRJ4 +GxnV==b{=5v2p ߅p&MփB9,џt|Po;̑>nHg61G=hMȋ6(1*9 ȇ>WA3_PQN|itc9 -u;KM՜;׉B7ZޘFn0C*;åRcLQ,o NUd::x(G&n 0'")hZ*.ys ? nKnn#;еQcC2r'xYtP(m,/ctr0@H`62J`Ȏ)H/ y}{@Z"}9_l˥ybgG "/2>d]ۼֵa.RYWϏu#Y:I?7ryҧ{`Rc|ޚ u#!qf 2&2yK vEw\1b"b^7f/hwW Kv m {P?Hq>{bqt/̹XV1ns$8OqIkr;p@/lG#Q˸iN|HGKQ@JޱFv!@}voH~Hb|3]{7qغ˼] lso._nfA2sFZH:tŠ2)~ۮDzϣz43&\_&'A2wǗukݻ&jOKA.Nk "KݯM\Vz7UtgVU:=)Q𘻰:^VQ=6Vxdo,Č}2:yoVrZ+GA}г]˩=¿*rF&Y\KX@7x7: i74u/] US=DoWL{`^? X|bb;uQ <&g ".8 G uBv> 9cF^#&ЛɀAVM5h-Aw<0:2hߡunw9..N" l&f:[,,RXJKT̯jboqc[>Sn(qa}ej)[,ee2R:g|$j[ ^=ffvHyٚ^6d؉A cRR),CaZ` WLs*aK g-X*J+E7[27D_ک/sfFy}J8ٚkJJ@t/I WihlQaF. d(**9;I42$XHx`R31mU;ZHH& 8]z͊9džjé FKwY- ոq>Ibz'~ ϔ:3ӉZoǏ:z!=KMwp.S=nê;S Si$Q1&֓V/QHK5.#1m`؎`pFBK?~ִ:!JD Ч7hA/N}W.{H#%OI Id-&hTf1EI$9Z4_L̦22d-O$3&@D Hh[=PRIDmŹ6#|I01E\ ? @4'$@'R \ ,bu,ܮR(]"]%_!k" t[B<Ä]M aG6q~AS5X5| ^9Hy-!yZ6Ev)U:pڄJ8I{DLUQM]$6j?rILP=q`?uÞ]r/hfa'qM$#jq9v[l?B,xxI!J܅?YLU$Ziᙧ1[P)wq 0Ua!)2?V4j Y,#xPu4ut6;r($ WL lSLW7q(G2֬Me}aizѬFAY;waݨ5`dpuHNZYњ͓1eY/!y*Iw5z8Qn|J{C^`u:m]- +[X7TGXxPbQlv^aT`9=~ H0CW\ޑ6Kn`g:/î``,R/xAQ;N7XS!`Pru-hn[:@LQ4APOP]:tYNTUw؅uBj9D\"p4~r` n$qtLuED~0񢠣q(GX+(J)ݯh0DI&pyPқ !\6,%;ZO!D- *ȑT*E+q2 `rסL &=B2?` 0]O|IF5II HF\]D ;:b$FMBmb\U4^.%*<BB#Hwv<~"f̔QaY Hv<KѣbJJzbzgf%MzrE@aZN8q')jIбG9 'JpF`E%EMSO0HN˛r`O#2gJ2d 5 ur ˟< ToJjb.T8%Td|%SlbT}=q!bcE {_y^e> wQASmd,.O,v,}^R5xaw? YG"ՔL%Ĉ~pRO/%u4rO C8w`ϯAx?;QfʮM>5gKj{Kt:[W7ۚ[kmj57h咜NG{]3ߖѥ$IYd+1t !~bgZ9$0Ir;Hn$ Q $KmY#UȀU`݅-:uN{o'q6Z/,Ca%w5}墎 Tu0ú;>‹pA0"!^}9$NxI84 -bs㓵`Vaxz@@ B>A/ ZB!0z5|.ܐ.̀F3VO86.Ƞ!i`nnﰐ +zD^"xbY~Ċ8dCөB]EBy@V@r~d]N7fpONh?*Z>.n+G1g{ ^2'!t8/`lƒ̓giBq>$ b8G6( '1!Xz ~o:-\ g棢u?ffY>`o=j`"B$$4u-`M7Vꅫd=>6Њ ~sk!Xp596NX=҄eC_4US~ ;JM^j%}Ԇ N p;D8DL@cU 7v1:87p%% .]*Zu;QɓU.lHb . >F]үqY- lp$Qbt%bF 2? >{ gf!ψ;[vt--lo;y?B%iP)b^c2u;,,zP,WMD 2k5SȎ~L/WT 4ڰz,o<*$=QmĶk볻6J湡 Q٘x ̊c$ų bd=<{ 07zϩw%8J)oN8q Q(y=f1p/Ћީ%Ibty vZ1e#AV"6 д$yF>"YBLa%f*^'ez{8/:?] VO3@b\HZv"ݟ@'Z̀T!7Q2>[kWP:]PڴŧH?CntE"cd[ؑgw0O bl*5b}Ư* 9y4̍ _af#wZy =ח{%ͥZJObmf8[~-TED0y7*oBnr ֻdЕa$ xo=_B d%Y>ʃF&`Q]d.&a҆|q )Cs(GοGX-=д*= v͕RJ jtr}x0fY/ Cyq+ X|G gz{aփeͿdWj&`ϳc߂{};0ϩmŐ[ }Cb2&l\*,Yx Nwm.nଢ଼<<- /" ?o˄T:/|ONz޼x"?&˿&yYlPρOSwO/̌Woߪ|їgEz Jq-VtA4xm!Yxא+Qr[? /!nRYY?z^4a1 X&TL/劦͐hSٹ;Eowb$\)OBdnPo 4 )pr&H{tW8'$D&#HG$j=xѪ+-'*Sh c/H9aA /(BIگ:ţmbͿ3Iߙ"rٔ#X(:MЪhHtt$x5ԋ?&saΏ+G .ux@Pp/b}6pc8;y0eHYnKFIGYoXJ$ec!P.mGr(XHp=H"W_DWdoDH_D$.H8j5iUa*3}AaN[Uao6oGg9J\ŷ9rY"aFN **RoqV~^#~Q1V3T+'P^Gߘώ~v 5vgXwuh1̙ T: )<*!:VUB lW5݄ l%V>WgFVE]Ah.fT^+ydȊG5>,exUPkAej~tXmvk41$c B^mMcM6f7J*ADAqN=I,-EmśE,zP?nRq̼|]y[ v2kq"Posېp׃Sx&`TP\ws.4w,qF=<">dbQTuIupvRQ/*Dhz\GҰBsҴfB]Q}VD!+L;B4^;܇n@dB$ R M '[7P[/]?Aw:SS7(Sgzg7 #-w px{tU GDM L  6d\ ;x{ED71;5-3'!Z3KH/-_~lTfJS$x340031QMNMIeb(R{K4s˗5A. ώduƕpKM @!3/D/&u$:E5oȄYPgqʪ5ⱍg1bϚ_,Q&Gآ۶~Zr8^$ ӍuWd%N6|IMin>Y=Caj*-9h45G+Z('3 N_|pfg͝_BU`x!1V rF))JLNtpaoꁒSszM)J RKR.gzmRsMCR5P̐P:}2gZ[̈́DLqAj2CtLUw5$D/8a+?QX笥۹jR3rzNrP#K=0ǐkex;e+dM&y x[²e*U cjufgz p+x8@Ȥ̦ 6$8J &Wx[²ED71;5-3'!Z3KH/-_~lTgRMJW^<ӑ_̊θت\۝sv=NM]jjYjQqf~^n6CcB .m*U.?#4,x{qU ˘D  ̭ -7f*1. 5nدx340031Q,(*-)+.HMfh\&*=9Y3u%XhTX͵X8cO^ #9OؔN*NU\UC /RPkN# RA.6=K"avI||v⧬9O($MZ>5m~5``ؒ(tb~t2"%}f}:>с '7STx;V4R/*$%md=.R긲Pi9p}/| gsC 잒]]ěoL)˯dވpxxѻDNόDSD?QBT33\ûMla3;~Ә ĶL2bkWTs KN4* `hzg1Sb.(3-ZҦa74aZh6!Z*$aM.#y[.,&xHaV/ɈŀdTTpD!"PS.s|m?_T%|sN\%uUgv,]b2g;jgƐU/5x_*Y%$?ix]F+n6 ˆpq63iY9|aĤByw v.+¸ҍn7ܿ^ȄU/PUFƒzpS:j`IR_@G7. VSD> ޳1pW6JۙF;٨3zuA6UF[>*a')Ml9wRTRD tOn-Hh]V*\77 {^[n v Zh}L:Lx[HbEU˦ d:Yd0i,}3hF o})$ye]-BC0WѶY􄁃gOEK ZƣE z\8><'B:C'uoK/ n8W VcU%XaJXA8qD m&, Mo[6$x>ع#QTzBAhR6t[O1'NlcO JAFq8:̉O[[<~L}ʨGSs-^LQ9g=lg;ɞQb"[Ud->hO^<~΋ܗ_ sz9u| gU6x)NLCsӵã&JI/KrC]e|tn\&𺲀W)bKkFQ閫%_yj;:;~}#Znv|r?\kA \D# LNP~]D$Z8rte hu짬5:|i LI8$ZN94LGÃ@G=1f,iC&a| z3-sMB3k3T{? ~֊C@9;Hx#Ѿ0j22f]!.cfe<(B@,kYuŕ~2UԑVQRWO þt^]h3a V%Z-/Ңp0dl\g eo~̧e&t! $zkyAHą\ Ud Eb.%Df5EURV<%6XCc-qz.HZ">5.~ZCW*P'i[֩ɀhZٰ&T,3 TGX8p1AU0'|@ZVEazN)NU–0,%"4ewhm aj ҾX|iBr|˃!w-JDXN.ǁbH4IAd^,t:K>-Y a< #ސ,Cikc)bi6s̀cm Lsvo4Զ{2? Y>mP6C;?[ >wd-?+r^i@53ͧapJ SV~¶R_[EVwZ_ e<)5)w*2TFwl3T9y̜z~o{ |qR1xc`V2vx$}_gJ"GAmc3]5@۞4#}#E=R>7d ݨูc5ԑOjx׉=Tj3-]jY촋1v =*[`|7Cm؟FXџ9}&_G߫c[5)N1& JxDu x/C[A1=r VXici|\if/s{??^)ŗC-qe@Ɇ2Wa{9xz 0'c";@$kΆWM5`րKA{HbGYQ6*x; 0m_ĸN\!xW}T?oKR($y$@ (L, Nt! 2ZVQ ulՃ- 5SVmnUAX[b3kE>ϛ@xN{{sy:jag紮ղ(";zeᑤ,"B.Ȩ8_h7E‡dBu^ BuJ 8d3tL*rIUEJw6#C7TTH;RހL\R&HI,mtC:PXa.-gξ-T5\L)XE"s@,s,q->LO۔IjlUѤ?Ƴ'S`[L`#keLIYhwIG9Bʁ_66IA%ݳ 6;$E؂K~oRkZUZBRy/ò @_F 11fY iR-_R6nnex'" \V8)$ P_ڦ/Ĝ$%T:nb }f:zuFU I)s(HұytR|wLc[D,?2jn:5>K-1=6M3NUWccJmJ!gD zO`7xl9-0s4ſj#(G͓+ ۅbϙR2D/ԚqW x};D<8cn"WZ1#&]ܑ 0Vy==JM{` ;}[ߜE,]4!rҟTAq;LK"cPGou(,,k6 $Im.>>?h~3 .n.i; 2*S܍,:5 \ㆇGw,Nd IF]Z$NNR4'1Igj.YXg?ά9r//o.S`QlBZ?mEHp+J_>-~m6tCk~8򷓄cJ#<76" *h,X7&X<ܺ ep%x2Xl-5FYCnoT$qKֈ# "8wB LLXuΉ"Wv^-Bk6MSL5ˆ F%*81OSwh /h:7fFj '>:0@sZHK]db/a51=^ƌRU犟vS-_/&lfJ̣l<8Xhy  !0&<+N>qǭ KPu~@z,xpe<>%G{}߽H< EW5jwK "$^ԌYL=Z[^&E 5r(R8?/$cvΙ;B#˜F݈$ݝpr6(a'F. :=zs \pMY57x3ܮ1 2003/10/29 14:04:597( x340031Q,(*-)+.HMf8dх%Sϕ4b8F~xycB(WPjNjbqD[lx Yxx[²eB0Y_8׍[o u,x[²e+Fy"ll>o`}j Nx340031Q,(*-)+.HMfsQktS: :+7z Yn%#x1!PP@π+(5'58J'395q{5#KQ^dC&c#Ii~&zG?3/D$rqp'e'gd'e+&(E14”1XIONe|Ŝ(19uk>v꧖$K._ x Wmnt2|FΓߗϓkjmx63 2003/10/23 01:50:54@F@ y @SPzux ޸ ͔&u>FÙB&$x340031Q,(*-)+.HMfl!.4Iqeo0)ezxqcB L%31R&x340031Qppsw faҫM[VxnQm7B&fe2(ɟlSտoJ5'h,2TM+Í./yE[ak8U(de0(O[Uum՞kC075(.I+`x{g YHwkZYPgTzVh jP0sJwo8,% ς՜%o;d4$5@~e'gu1{_MQ2ſ*+{)V]fWE9I@5o=gо+!.EU}z U0CR'(19h.7;dvԉDvCK̉|ȴmCb_C]v` lh>:ʁ9zƷ6z>2?r3YNu]Zhd/hAo{=>(I{<7@&i\ShK>PDk0OY+rcIp9L_tujΈs٭@eDBF4$zM]8*?T]9jfRAdr:zp=h pzF,͛9yĂÆFX%+0.@7VO& 7yCI Vs7I9x*K㢑XP[ҏ"f_.K/% `C畟߿5U~2٭Fm}*nKqnԔ@j̝qkh3^*K1e?jpLn{ݰ/;>oo^2m87B8ȶ37W[Qzf ){FooWHv "Q]0GCq3iF('q4&Iw4-{JY04dPQ]bsPI1B8hEfV !::H3&8Q}:\m6#OrTa!{+#Ol x['Bh1͗1%ix340031Q,(J)-`( ɰLstyG&`xcWRZXʠcp[a3ҵ:3/x+.IQMQ/Nʄ g'eC|d)%><MO.Fg^_Z\h!Ј$SaSxSKhQ%&V㧭43ڪikښX)HUDoBBAqQօPAQ+4Z09s}ws|&s)j _ZpT3SD/H;E3KxzNJ;vEbRxw, ƔYE@Q^O|*1&%3)I+"?BKpu3 I}ˁwao4 /bQ )+Xni.KdHAe^Y>q VTEow!KrVNbSk6+R 5pvellnf ZXU_ȟUGzjOW7To]P#(zAܘUrZ ]&cW;g.b>Ys<nuc& ฦfuoE>Cp^: JK؋.4w;y6>-EGi:%:;-@\ ly Xih2kG:-\ROC*e-O#576h.Ғ[ș{\LEq 9xXIfnMQY#CJv\ ),o)L%yCVM.n1(l g8g|vO>7`c+šVs>e2F.WC]İ;7wi +xJt阦29F`]ӒdQG!^4 8wWhlG{߄*],0ƖVIynXd[m tTEh4+_j ๶Zqu>͍qmuAӰt\8xsd/&XȰ,K 5sp5?+oxX{p;q,[~KK#Ȗ+CRNg>%G2Ikyg@Z e@a(PhI; 2)N(Liivײ,g~>xsի?^uFOnG/ ܬˍtmnج5v;F=U09ɜڅءr)mzu1ݝС+W㹛1ruaN_!F$Wy_rFKr )VlssRxn"ŊS)ĦR*2u=f-D JH%F)2SԝFVLPْ GL#Q,.K'?XqҮQ7J2Qt(u6A O+Ipr][G8_uv:мQ"ESYO|w=qw粲YĤY$oPO uH $uS gpa5$oTШ$Hمs hCebںmL7w-=Ci픖<AݗO }Oԕ@ 0z_ٻ){V74LѣCP40E"bB_-]a\tvEEE&z)5Ց<4΄"m"3d@X(z\>Wz ݈ҶHtwNwgw_~I'e^ I#~j0(SAԣi)IJp:YY,cFxc)NV [d_gd|d_ANC T\Je4]%urNiߌ@EJ*T{9rG0Zbk0OZ4(nNfUDgєYi6[;J|BCile$XN,_·8!|`A[& DZ="F(QyDrg=^v|3ߦ(2j2zsIQtߺ1&=iv:}TcݿSZ 8c&mZhoS$Dgw=1ݻAokӾ̨j]'OoȖ` ܛ?ܒ 1gG9=K> yȖ,0q_>kQiwc4ҎɡkF|9|7af}l!~9ٲk-7UG,7Čp$#0:1Ճћ(ksc$?u|VJ%Rs p]LrK&sb-{%_P][" N\(Q/5$jM$*: ^ޒ- Z}bɃB'|oL|(:WXhcA 5~#Ek(:/pV8>x#cNNW(e) cԢSp\5w9¥+PWW6MB3dԩ>lRs2uJؤSkcm b-!ڈZ\u1.?ˈ%"j^kU|5Ŏd1+MtDr.)7 իa/G054#[b PD c"8IyxC)"5Z;j`GkDl@ ?NU!gE @Nt74)F# @bed+AQM%1#!xiO"ccaQ? 0p< _uG¨ Gy3 A]!0$ b"UmoNlQ j`XFTn [ x:PdR&O{<4DyyPPWx1&*.MDQh'C(Hri#S6e!GZrdPKCi NJDK}N6j~`N*| w-c~Qʦ8jycZl~:/I9fsv=q{G7$xv-31CHT,qg/C6F9We˹˹(쵉&残V-cA˲7,U8cÎ%#gF8Q>( m"Dqdv('P7;[lcVN>KӅY=7,ޤjayƾ5Yl9j=)LsXS+!-Oԅ^9'n[M.8/#6vLpqox<~r8G.ÉqxDч$nziotQ)FS0x8)˲tFNai7N&M;͉>>s\bczZ(" EI'E[:}cpp1,3NB[Xg3-. H`Α_)%r6/c0߳҇XhߪP3KxqmrdVF@?7D̀ϋ0*"/.j(!xAkoM_s@Z^&=P(URKUq_IyU?ܬszB}_(Ic3WTyA}iSԊ> 5$D^N$0Fx)El,kߌ0W89cN](TZ ŴQ"6EFVk*a."'1p8}2*"?.-l$ x;iF Gd L#++#S̼t͏xD3sJSRlRK2X?3oki4 _!$8LB+,$bʀjJ3 ɎN X\]|&Qg$r&7:NVIprEx340031Q,(*-)+.HMf|7tЬ'y/T X .|CGxRM0W̡H$-R`A =aMtlV`HKgV*4Phr$KB~`&A7= ֕Qyþ"eX"7g%n}!s9a\;D2mfdHvFN2oN7ŒD#H5G9`׵k5TSK.V/0FE pBy2HR6*7|-x5Nя˞ϖݎr{[3!z@ !_ 't6^͇G5e" P"%DƲmKW؁w&5@`cƲĬUǧA".w幈 QbyEfv2y}fm.T_g{چOvdwn{C>ggU-K\ \K~%97?3`Hx|e?w :Rx83=F?t;ߢ~lGYIa[Ghh&쓴0Gx{e+ VޯWs7 [M LL2 RX"=9zGk)]Qnt%gY}y¥X?z@yMd 2vk}[M&J 5%Eɩ@ g_ti?W4\g0/;%>zE*5b~|c+` cx['PhC\͗193an,xۖ+wb&F L1nFx;& G6bjA*9Bա Ad[Liprutils/.git/objects/pack/pack-37701271b2889cfdbff58971243e26e6b15b1411.idx0000444000000000000000000016632412275251125023440 0ustar rootroottOc"+19BHMY`lnu&-5?GP[djw} !&.:CIPW^jnv{~ &/8;?KSV\dkuy $,19BLSYgqy{  )1:@JT_gox '17=HLU`nu|$,5<HQYbgntz '6<@IOFF: IXsqM9yO?_l(aAoAh*MU`!o~ )jY(QMl# O~!ʢAN,xQ-- yfiEXf%?f}NjyGo TGWY* zwdJ2el \!,$|hP]>n eIʙ|Jpo" ꖥ p6.}CUm/ƾ} '3OcJBuN@IiK%†W 2tA'jUZ<+c)o~,BI1X % =µ/KC[XkzS-;n725Te^p<]P&)uFQ21 ,B.Щ@'E__`n,kv?U~>wW^ \nS1~6L O;9$kkqYIQ+3K[*"\$W2lZbG>UDAAV][l0߸:*AߜZAgdȟ}7iif¡yB$)8DӓsC>I\\ƥ (cM֬!Au]>!r)d]55\Y#8Tpp "< 9mSHn,pO=z7<( laukzp<'3Ė0&WLvd>TwJ1CfGď -4gu"4W?|jOrcsd  bM?ly7N:3|3M*BS!s{Zq(KzztQ;^Qg螢< KR|y/J 71/e Weq5¼ 0y- FgbG ߂#ڄKh锭0 -3n87 *F*^(<#+} 0. fcl ׊R D)ͶEIGXR I6.tUnRQ"~ˣ H ڐO7߸ U6Ǜ mΓF =9c $RT4Z g\Bۺ p?Hf ׄH #6D2 2-]mg YT|fW \(xpcQ 1w:UE$qiu XY#ֳZD `u*AcUe_\ r: }p_PhnI@ e bD E(2 `k*}]<m E}G6|Pj~o\ S2vƿMJ3 .Ce1RL4 0B`Z$F8T מ S9@lWT/oREz m[hMz]'T_m iDD~>,a| WCIǽS, .tE;G寲l Llb8>CNtMyؐ @"KκU}*vKEǻjkr}r0}A^/bQ;-,KS!9L4^W.a&pDQw3!obY_bN^"S(U71BnV+4Hd$`iO$U+\o`UE Row-1grKrc!Y8F\%I$eSk"9b/J70З b\lu:hzOئ Uʜ@b/fbk)UpLO;{hNb&`ydN/;v;8b8PcwI$ȆkКWL-hPνǪ{JzXcR3@Km:uptԽl7YŃ۟澦)uS ;HҞ x;' iaj-~-4QF.n0UTQ?}詢=Ǘ'zZs|yr)kJP^rSLr wUNKuWY[Oxzm}!N'$Wk%q]*k˜5wqOeG0̈8?ށ4ڄDsq6,?K H"yӔ(4I3ߐ#fCZCo`XAm_04%hG^- k?tOztlJDf60cÍWReȃ '=P7HQd$my*,`&yI9IBI}6'"T,J` tSu:Cj[J$UK\5m`x:=m݇0 D&NֲQvy8D(*{`oc$ e sCR4pBW=*qd$/jw w8 YS\y8TJn @RO[c*}\[牡YOi/ա>5Ӣ#4>4,5t[[|yu}#Z!ɷDO"SмխkR4éUHFk ]#8%]`}T(B;]79iL2 4C,LSQ|(6AT7مmT!e|AKtb*H;*[}2T;`a o1J]xF.p{QfZUj67f3F܁W}ECT,Ifj Mr(:`dZt$ףbpˣXi >yiW||QQC~Ϊ4r FL!g:3Jfl#S)IC9a4:'DNk#kقf7'G 6?m#?sLTRIZ8d*3rsXUN6Vx}5R_`A VěQaM댓x|Dg@鿩qo 1%0޲c݅㍌I'f袵:K(#eKDQ`wbI“;M"T5E^5)9(*<:ѯɁhZy0(P[X%)[!"3gt1Dm.-geTiEz+{dӃ Gw `#I M3OL ҃e@#{i{:nүn񘿞e/'  x4?jq] 9jD; (>j5g^ 489ӂc<عz =]83 y-$ٟp, r9$NJV̬,I2 †$(3?[~Oy KS dDFJ ^ЇiCeNҙ %k7\gAw `E<[<[k4!'͜R8b!$pNd/]0LBQ!>X.!|r!HVj ɉ ɀ!Um⑔u>\n!ș!y'g/(0ʁx!w*Kܸ|QǕK!y:eS,VU#!u^aXjnPN "0{4gCh0/玵"''סW_*XP."!"d (=9#PFJb)T*=ϺiQ#lozu)(L#W-,1Ush.QR,#zZ:)CZT+$@!MZX(c9Z$gKɯ vBV$p-J;? wH{$$5G{|hs7l$(ɶŕm˛m,|$8ݵ!m$`Q!u$$vdbz16$qjOl۩r j,$qhnξUUg"$7l#bt:?٠$֬4H|*{$(A=Ip%pA c*jUG%@h7>'왊I2{'B‚)ў<ygjU*,(͊*(T"g (NoM\4X xoqԑ`(,8t%.ζ/mG(|ԸE>@]XAf6(i$#X/A(7t<ՒN(\8֕ӿ<[qb{(hplb-Td)2*<|=0mեw)`aK##J+IQ)_' BZW1\?Aف)+)II[6Tx O)"=Z8ׁ@41)3I<skPvX))~:p,]Y5)w^=Xr?A)l?Q֭٭:|*6,яN0:3*M, **+ 3b]FAx3df_**=yՒHc 6Q*X~ ͵3{ qǝ*p?]GA*pk+BZ2sf1*)IR*T&I[sX*zOiW:r*76>Rn}e*7>m~|0dv+$w\X':hyw+ Bj"7A}5<+(RYڼFyѪi+l0"/kB+=q<2JdJ+Z g"'i+sNX{.`c+B~F{U{j6#+'8[7Wn/i,9ev#ӳU,=@}/,7i,~'95u>Xz",!8eZt^rP,עB^~1gb A^;,mOɚ?-`}- j9եֵsܬ-Z-L$fUa.jh-]\{,Ǟ(-_X6Ac%Vo-`e[[54h!n-gOlfU=B^ )5η&-}'C uM%\ue-bE/,*9-uYt~{pR<-MǁuF-{(y- XQɸ>G[_-ƍ mYp-yxtžIu.`;\L'd*a.c3V6tBE\«.ft>'^u$-.iBi|tK9kh2ghoTVcy6yP2c OPDR\&қ2;|URFMŀH2h4B3fÎ;(O2]IuM eu‹m|2Hѵ-(" 27~_/H2(}˒xor}n#g35ymԈ*a3#k>a iP3Z ?^ (zZey3o'T>0o6\jB \>3Ÿ۷Jfp:3$z`|g,F3ߨ6JNC-%ۦLG3vs<߆~04`rA cuג 6%Gv43tg聑= k4,hCp 9e4:pn E[=U5<E2O֮i5Y1Yh([5 Wi =~5l z8{j5vTG*$j 51CM-Y:nQZ5 !5-3F=m3^-k5mȧ9 J5%= F+Kl6 wPܙ*u6,uⅯ7_(f藯t6: uIvTWjޤI{6PamȃN{BL{ 6T4sr%hqY^t\6lZceV]2]r~6f]t%n!E /6­je` z6cՕL? fH\6gsuξx \<6"zՑHP7PbF7sA^@7SbQF9:Q`7Zа0#b50@N7i ܝ5Y4^S7raR `q1׌7rЩwPS(Qh7Q :Ť-:7M y1_*#v74%tD8PE&wͨ28lTl{޸3Qf8othi_[Vg\8yJUA-p8ʛV991>n&hs\V:e}@9@_C,z 7u,\9AH# -D ׭A9H p\ GvV+h9V0 U< .xy9jT;JVM Ё9[#N@7?xXP9CO׽ F۾9n2wY%*j*2{:Bd. O_o 3G*:DP:Ao햊D|OX:FD;D=~zOB":K8B5{wYJ\:LZ65& n:^v3;aGݷ)q:fQ3L4:ɆBo'dzr &:O:ڙkj=hM5:/O, ٢⌉": br=MEhSB;|2-76݁z.so;MK+ b;3'3@EвaT;cMî,IKg8/ٙY;g98sttW;gF_RM(*Vķ>;oV 7kg; 4" OM<x? JY.7< JI^{hN7dz TeCwB:}G>1,i4E=7Ҋnlb>~1:8)yn>y[?VjSueL>є?֮Gh~Ĥ>ߦ$E̻K:߰>w?h1Sof u+8>%$[,Jݏ?#w>Bw:?n@noMeն;?ԯ$k2z@?[^^ Lf^pѱ5?h u"䘼pn6ZlD?x-k*>(e q @vA3h3f@# x8S7G@>24EZfes@6F ypQWS@X3x #ٓLN@`Qd=LI@4K5B[WpP@G-ƐB޿.:ĵ$qCeLHb9=DNt4"fCz6 2/TKkcn!'}CK׊A§t zFCm'@~2o 6CC߻CCswxӒWCRd{CzJUUBC!5y n\CԒBZBgD%KX|o& _kjDFB*EEm&/AtDYF狴mҾtTD`}ׂ+$W.0ylDgiQP% ItPDaC.ZqwjBD& ,"r.QBFDxif' B3D"EvSkk$_\ܞD%ap%cDN 6_Q3DW4z_IfSxP,Ee䛛[v?th0ZEW2q+ZE\8O{cJjV >;ElIpc'{WE3N%v|XED 3C:U>jMME!東SSak8hX, FA>ʲf6DZF+_#=~cFd^*טa4?F({ H?\gR FٔF3}:"++KFF绉XCnB>2G}uvEq߭խG6;~RGnO&MPhY^wIGpeC-BG=z*i;*`fKG B[{&6%Gb?k|~d>oH3OfԛD C!ACHMCAXd#I3Heyk>yA;u\jsOAH(M0m~`a]H{< 0oݽH.v mjo`ّ/>Hٱp010\I.|ЌVW`\IK Ow@<4;rIN|(,JFhSI +:>нڪ:bID :h?0%ueOI& iIؠ8k iJ&-6[74k2I`J'OrfA8J,Q!Ͱ>4WJ8ѿtpz'Pjv&.zJE/f~JI ^Kn-k2Jg/+Q# g&(”,ΥjJ_}-VH6:&®aJSE.ʮ󡔐pi'J(*\+ܞ^{3J?\wA.A)\JZ GNmz@+Kf#xHiUSKrjn+$6DJ\KQFo?TFK(hC&[qh|LIĉ7o@Iv2UCL^/)-KhEL펉^v&w[5TaML:}hki:<3]LVNpU*WӽH"Lѝ09%YrLŇ_ &B7S-~LďϏoc:UM@%mAJD:MC=n"<e)H1)qMx5e,;/ 5QgǞ9O0\w0Q8,1s)7]>MQ>YT搂ty QlajvJv>Qsݚ.m?;Qs4Hq{+E^QPmfcqc*QK6^iSy+"QwNiSiB ːR Sinh\C_@BSL5_T1J;SiE?EݪڗlSOy=3U}SKFe=(=M4k$vT  O,8׫ETIg4w7#(?`W6QT*6"4^/yt T q#T+>% |b T)JsUҕ2މ&5UIOt!#Ux^}77 (/9UH9-kKuH轕nU}ł:u>/gqg QU5 i92}Uosv*9jNU(8YЉU{=gM!8TU؜;`{=srS靦Uvu#"|;m]TUcV"7巭"RV%'+ES O"߁@VoP癝WS*Vfe<8PjOĬ`0V}8Fq>n2VPJ>sVoWTA9xưVڞ2HDU ~ՈVsQ_0C/E/\XV3]h|=R W0_'Q7IWz/IO|\UbWKg^}w,W"r[O[ɦz"WƒLޘ;TMMWҌkg?2WZ1ئ{-X,rBe+׏yK.ֲzaY"m_sjh| +Y¬-%<_vm;/:$lDKYGQp_1N Z 4!E7Ze.)؊^N7 Zsn*(v-n!)ţ]^PNFTy cW&A]v㞣&,9+t]|¯Rnƪ7M]@S@j^#0dH&]xϝIң.[]6?쳠xIzc]QpDsBɰo]wgT /&r-"]٬I )3z6`W]WwIڮ{Uq]އ$6O ]ka+mipS]v**X&1iGB6^<c=C$D"^/+ n3(x&E4`;X`_`dx0Ic,tUa!!&4*' Ɖa08N٢u*saYp <)MO$af ٵT&-uj>~ avQ$x]i"X*KaLaNZFxvU*a} |I5;UaG g HxNrb3S?e~)A(4!&bVCYs%5R78DO]bw):)-Xb^%ȏZ @8ʘ-ajby n\>&wb9 `wMڹ) b^[wp4)Bsc3JI-eդ{Dc z DO-,c >iI:^'ԚZZc$+4Ͷ6e9,McIV6&1#cXTUWqGtxc*85btQ"M<\c'9FtcHĬc6͍Cuz*x6wcJl An8eжĭ^n{ |2 eN \eO"6 zof q/nJǎYgyfHapG7Ef)EY/BdLf7:'ߍRު,f~hTV)`rkˏlafQ6E e[CY#_f۹B#Yyu8f۞U Ka+Du~fLjYVW0wJL?[gjz"}WÐ5P5g{޸fY4yOgݹ_;l xڷQhj~L^! LhVv-b"Xօh֩j. sϛ-hi :wp>UiV9iT)}{4Uݹi#̇+}U7l1~ri(dpXIHi1Op/Ҍ"9!giW&3hMݛ5G)ikpyCɦս:bM;i;i|Is 1XikӍYDv@Zi/Z-cwT<~&ijdHœΎT PirCYc_$vj$j*3ϖ+jF],}$+:/azjp3Y^[5&-jY\",RTju,hDv\!Fjv^'Yv j<' !" j0|GZ^F@0k@>^Q! zԠxOQkV钃jDQ f kB:FzyfYAUl -qgjxKrBn{tl!ubӨ-̲ܽl@Q`_$kkNgly ,c W:OKlu/NΦ=!81l7n*6MhC^|{m$;IF)Oq4m$|$ 'Voeu .>Imrn?^(O,m'|dKp-+mz m;PDwfr&Nmܣ6mr$C:wr1Mm@0tp)Ogn-a[Ln0c"nm'`en}][6;'nfҗ4rn5|{n--1'(=nǝrHق2To~Ӕ Ғk<{j{ oqO?VgoG:r˝k]#> No8Ƒ?ɀ}*Eؾ)co<V8=۷P󹟅WoDT[0#cI@?in6oO9ZA]oZi^oHx+cfov_9ŭ-=xeoJ?M[ ߋ{ p)z";m$xTp]`#?=iFu|)ppjQ;Pp9p}h[MOd`zeq-N [w9p5qF CkI(޵36/q[M 6e W#L,qv6<*2D)qxWP9܄;%(JKQKq|̧4_ :.>q~Ր(_ ))4qs+'?&_][gfqs$Ħ>Vzqo-u z@!rz\tl,HN)7'r6Wގ*n?r$BO8YIuy;IwF*Nm|t)[KԽ)0A{tgVV6;&gB "ul_(Q^khuoV>$=Ae4B܇uxHX[OP%v uz  4qu1mi0Ct;uh{m% `Hz6u܃sp3ʄH1 v0,P, TyE v1 O"M܆8@UavDg.D֛vv-c/r% v U q̑ʔv<:=X%k88^uw !Qx]+w39OΚ^Hse|xw_h}ʣ"#dҶH͆wr>}2!~kZwzS&<"HD l|FwExbbJF Aw57B7 ϛwdZ;[V| X)rw] Yt̀/ܑdžw d-0ќ%^6iKx4zx kjx$pңM'A)x2zWe Y"x;ԜJmkIxXe2e;T? x xq8HF^_Q*xlZ8LZUj`y IORpx'yl1% Vfy ''d\Y1 yjc]*Fm~\^6you=lп '/)ly5"Fz9byb^ %pUy#"+9]m0Ly2cmzzhj]x[ &Bz6+݌E0|Z8k!sx|y<$f]S0|,;3XeFh0i~qkR݁IR1E~t8ZXuxϐ͵R~64.Q`ZyOؓ<v tx]WS!H$с^ +cSIH.hml֧TF+W/@u3 TVEu5b> `1IbDaaMXO6(e,6ZoK]!e'S oGX!T\zܭP~i Hk3 )QˁS[zs?z̀4Hڷ_f5t4Vk_ zMAKNX\6kfWt.7瀶QayIԓO cYSH[u1 ^}D%HJqֹͧeQJ%e[o0."LہRٚ?S4,%46_P^5DY}FCDnoߒyRBsA$U zw4F*YeX"Ȗ?F?O>D7m ~h䱮.9*"\_d.{|>|Ac=v4ozE|m׊tIqo#ˁw{CX||v? tOnB2G}f8˹̋g @`!l+"3t-$UML#1*j8DnOkFoؓ [ATna)kc5;aK`UO|f5ͼxuV~ 3AO>:j^_aK 3P7mRqhc@j@`l3~6t*o-\u{~7KuP󷃊|5tD;V<.U\}”\MHH߱˫T'ŧ X8{E3v#=I Gtm.h*2wgQPj5wJ[&$S?%%}ْ0x2rVA/Y=[.q`M|qkXphKm!ˈ{Z̰]5y< ~K*lq}]L}ǝ yoVuvS*ǿ:&zL=#ۆ̓2 vݿ>yN;dNx\=G/^l)՞[w;݇k)@b@o%nյDȪr{+C= V/zLmqτ8KEY =!z:{Y&.!Ozۉɹh[eM,y3)-EXۘ2|L6~<%&Mjd$p}Wg| E=ʨ{Ԍ{[()ΎG7hoo\L ?1wXSil CUXYڪ&Zʚrȃ}c O L4G\rBSP+H<9C(!㈸H/zQA&Uuuխd J.C@w}ia𡼬Cecuof3MѳPޖ Ӊqlv*71oJqX wH;;J;܋t[W_yU)qڋ'Ӻg%sʋ.C3xFH.7G%á{_*Pa?ᮕlm"2vMUf qy} PH%-qDVPZK49.[tǗ;@=FYr)`zW ??Zn#U`qC~!W &5?-m9E.=bXb'L q'ǁp49R먐ʛ\Ώi-3IS c8@Ȥ̦ 6$8JSkOo{<_;@Ugj|X^q{U#{vG*}֐d* =j2\6PdcA\>Q}<ِ nB!{ m%˖.NtuZO?@ZV& 03bO#P-5١g"_Жz5wuC[]|*Տ!"7eՂ-^X5?6_괙‘ MOǘgG}1T)AV.SS!dZg~-−9+)jmJמY+N&cӅwLk^)nyRP`̈́} n*w1gmpJM>`;ivNJCgAyv5BMd=Ji 7+ބZj5ᮌ@ʛ[b|4 r0m실 ƼZ6La}V+}=]7QeOOM(C`s Ǽ)BҾӅʘ+АDO;WһL vEJ !H|¢)݇% Ve~$\+tЋqD?f|X;7$ߓ/,0 ;:ɔgF*7˿hRLK۔D{ п."1͔I 9<4zH(?L*!:h@VQͳ쀴FU)2U5ì'`ƪr䄕mj!nye?Xd=ltY!``lX[˕%/b~k $F8ao5Wڙ0 `eVտ 3 3=Ń^@"Yܫ tdm%ls.":SFې(` YW9d^a_̙ Y1ӬDO7xg1ύ(8yZvmQe.uw].[|@p+_U%4ަdlMZ!N_ CR?$KUz,њ4۴S:b:";>Hv(⒙[妚т??hu"M{΃%*ūrb3՛}Z܏U[)+uV/3ޕPefAԭ6gz%|~䜟3^{f@}Ϝ29PbQ5"aMjWYaǗ3mMTg=Xg6 hD'KGp!Ю|rnF7$>J8L[xbQrcMwEUP󯣖TἽ%ZQo΁!4>JqVpZbUڽt G|]fۊ,tZ=Bw7LUT_ N8toy2&X[.fPNNbBŔRVοG|=<ơ1X U!z憯A] &=Ş[\I,/YA~7!WeJ%e%n>@۞mTw[MCQtbPEp1 1 :CΟ|ɞgs-;g\rHU9򞴷3v8 =&SwPF^1NzS<rh@}^78Y4c^ϻ Y5Ӣ I(Udqh3H[GjgFL&~}<EJS ƻXғ?>YN}EzBռf%e1E{SdDfY?˛̱H*:{o]e&9r+3މF'`@BiHlNFm|o&qIeξӤyKܗ9|tE^KzoK!ʦ,#N[vW<]6"{bA/nT t7ǥtD/蛍(Cts$*6ӱVh52QHLּ̓’|:K5Bl6ɽ襩GH]TvAX!Zק; kjۯD@\}k+g@4XGZvwYd!5Q/Q_)2{4WŦ9N'*f7Z+Nm(lm:# \*Bܭ IIФ֠(܊w8w{Ka;>^ԟb%sUp%K#٣a &I-Ừ}VQy5]!ᩯM% ( E)&U(Ù=qa C-3*d>n T_r us;fz+:!a}X܌[ЉX^Z2\5eXVv4|tzRҡM ̫Ȱw_TgSɍS*1}Bc&u/ ſ3_,0| |T@X=D*}~u`U-5,Fp87649E[^褑յS"} 5,l_HY^RUfPʍ? ˬathO/"et!,"jXeQ[;~n&qM6="]C|~Р3˪-&{=o٥kӢb6 53 i43/άh'c= í@άV+TiӀ)A%imlF)x7ΪZI(pqlF fWDzh%Q*:+IE|eCɍ\b~>RHjGbNc9ү0[248U1OT߭gt6COg"Gт5~8wsHI= *(/- 'Yt /"(K-;ErBf1l}%d$7ic05"MBm7fNExe섔,o< +}uf@V׊t~`#Q6(n.Wh?n_Y(mXGPsyc3*I6J~t;vDw#K][ ų:fl*e 6OE՗?`Q*)| ǥ*DO}b@(fa`2!t[6)3WA'&"f#)A9pL@HeNփ$mj]fZwf.RºB3ʀ!x1yQ+$^gw:˿צRѕ2sמڨX]a_Iit*uDOĞJ=kͯ+a.aي9(T-U74Y4C[b}-$D404^H@F N"vɿ.( : ~'PƪtX3,:56q("5PT|"$RЌ_-(k֫XD٠UT Lkؽf] onk2o3HV߽ڰ~о"_>8>JqI`ij"^ DA C82'x[nYz Tbٷ5_*jR9.!LZ5od" s,^4݌}m}$<34)QW*3axl]>|Q$T"ntWx}#; [ ¸Y=wbMYθT8K)>tSj>L94G\)_a;-<|a[*ᗟM" bRitv鹄>t`ޮU:s{I-g&mw2 _4 uX>͹$߃Z).` }vpqEή=,ðk_o2?ѹ$h  1;FȫwU}z^\ G)8gR!'$X 4`X{Ed:-aV0wLW@YݓB?$9PGԓ#T'So2BԖsJ\K5tH5YcmU"Y 9͂.r]MlJyA%Y@ݡ#q\LXHU7ͻd5 Ũǥ֏ix@"b;:uiˣLcFk/(Ffc3ոI'+alzZʼA%I<`-R1m_" >$Lrл@ɕ=(+p[<}_6"bv~-FE qzmx (E5Eqٚݢkcӎ/ܾ"qqI} o׍*mM8[oq0VN(ptsԂLET_w缙c13\kMF}1%ս5[ $'f4uV0bcrgk0ý31tO1 lJ#9]&XWt\imື-Špz)K~GҺbmIA}'b$"tuZӦ3;%]6tHG:Ve*R&ӌ|r.%XNÇ~9TZ$$zqM+UslƸ&r({'ʘ&p[4! "!)5ny+Gr`8=Nf2Q0oo&JQ X6kG*"+[%,bP[>ea7 r8Taua$Q{1= |(s%vMfZ1M!<{fJZ7%e: $7~]s etolgi *:bkE1ppR&!etﴣ/Z2QQQ|gهU1`cԉTޭyBOK)%SW4-muP S`f3_PXooUD4h4]i? d:޺f dS}\Bؐ*DIwB KTM-WDƒH8HB;8~_QFF fGn<ʟ ۾[\ʓ}z@n 98~,[Tٞ%!+/=h+T_9$OP*.IHqh t3LKk4 Ǜikn!&s;ǿ),Td22^7K$Y,ǥGVusV.UVv4o|K7Ve nxW0'XzܞUCMߏnFnѮ ? Ȇuwώ;/Ƀ|6ҼyȘ};/ocμ Mfhlțzd>P 3pȞ=|hz7l ȟR $xF.sȳ-ZkQ<)h؂Euȴ'Z>FȵƂ j H.[Hͩ영j̰ߘv 9 >"I)9A%errO ڗR|`v&K] 5 LM̺ɲ5mqXJ2GnaɲɭL ӗ48GKm"u3 _1^!yOG_ohฦ?K @ dia):~[G_FIC@8G^(T#E%%,vj[CYXrQQ [A$ ^4ФÝ-f3sGr"TKٙJ|q;MjFT:Bm:ys'< p4AAoԥJB1~N7IFgWY"o *x^lY?ޞYRo~am@6@{*I6R|͉Gb1Smdn>]E~u0r h˕1 :#SM]ˡwMj@iq˨frDV3˭H]ye '|7+jRy󓜎?^u zXѨ2r$;ۉJA9ޖe'Ll_=8ofT TՁϿ.LR^b/C6}Sʼnp6+ґk6-߫¯g#:T1K906)(J<'R>GϨ4C͌݊S|mє* Z͚feM/D~栫-~E` ol3JP@Uufā2PR X Ua$@ʈ3o|# 4 r+Qy]w B2p3k܀pjoDeaSN `z\e@MZēXYg+͉'GQ&`&,sWQw/۴tOaJqLbMr7Όp<՘QPRkGΎc-#a,pTُ^XG~nyR"K UL.3!{ɕ ?CC'uKv_D*.xZONl[]H\ԂCvWј@ang,x˖C ۜ)`QN0.!qyU]ޟi6b}n_kKW琢Щy #CLT__4нdL;~lHu.fԬl6BWtރsRtԦqcjKɜ2uɼX[04 +tC_ɍ鄝2o+ڵ\7 ?uT2>WiV~}~F`*PlNte]k`YɦEFjy C`~'фn4[k‘Nщ656Q`5a VV5`lg ѭ$# ZhѮ&(.yv@ZgU&Ѹ+v#[QfSJeD$o>j0|&c"]vI)'&<(|TRsi@\7}+PaQsG fX0ݱJmQ^:k9@9#]7 tvH3mQ/tY|P ҃/@–|kZnҡ/.[E={i4 V. #hV?&:e5:FޕRv-J6Cb7til-ZX4̢l$Ү)Jӵ`m# A6DnLTr6;R}>17pR"r`pWʂIq,P"Z t_7&UqeӀ$AGkMgq.V v=} ;su:8UWpm5$. uƷo!:!$б]hџ,<<ȕ? E fF<7ԉ&:yoD.ivDkб38%άtaPl9o_IzTqHhT@ 싦H[ͽM4ր=#V)։HՏtpUq֧6'YOQH5|֭ dO(fޮO< ~Bk[Yt6Vj:6Os]A@CS~XgMx]Vi`Q⻮Ѷar)י4Z~7~wJשz YOS?r_/"ױ-έf(i/B&죵ٲzdMqɳQN֒z򼺧Qm/Z)՚r)rw$4?pw`fǂ`jbeb]]r5 49Y0Y &—:ذiaK]5EA{DE@[inr˳$~UFZZv%'R1]%lzRwrg؁%n y#[Q͌ 2 W/{7>\3PٌճrP]L;Z&ـ$2NJ 1v ~ِ(?3`;sǓ V>A8fbLA8;uٲiaY*Ekފ ;9d8_}wsEuQDiS*˫6_5$y3+齉L9Gc-_E^FOg!8dB&>( \;)PX\8Qot"jA2ISܝ\Н;VkTq9qet0}wT $>}O6f4t:{1m:J\;Ez!} W^rPa H%+ilXYjvqGD'b G㳷%Pè`xonJv/3 ΋|E .-#,7TOZjGNCG]izr=a]r ex0N)h7B`l9Vj)!㞭>xˏ%hVY0ҠcD< ݋rBѡ_G)udi_i0R#\$OO~t =o\k 䄑5Aoa$:䫄p^!mWVz䳭 4JAzF,I8MR  PM'E_8Vzz#)&ϞzC+Ę7'dwnB:2 ]7f[L`BK d72Zf "p˜.~_}roGĄ$5eķ5Cw,RqAC6< 6E13m4#|!`_慷~['9YCJC݅ZLyafY 8kWLX~& 䰪]+wZI<8#[?y#`Q~j2FA`T= VGxֵ&; u-F >tl牼5 ,MUq!@@2ȼ8" ӾrbwRᅤu~ L*vjaJ-@$,^l )>7)Z^ir {ȥef>- ΄e>'}3Py97ݕKV fS)wJZ. {ک/f#v}謽CqTly<ߖ|ĎJRJ}Pjo醡U9bx|[mOHpA hY1J0Iy8 kSZ$C`|M]tRꘔJٿE9[ǐ-&;f!~lHhF}KAUt!IMb]u`MpKvܖ EפMxH]A뷪pa6OMDoam)?4Sw(-0J/v\rSڪʨӨǞyfE`m=pk+`_S7dg|`Vٞ-ֽW i8..XGcOSܬ uߍ A Qf&/Xjx̀_ hruK~8 @/Y$Ah-$h'&*5t9FۃW2LI(Ew(y˸^?qѲLU3pmEVLJfqqte&=OhϛzX-XˑIX Gfj,F{5Q=.(YȤ) b܎%ta&] ,I#B~R\O!s E95!#4뜮# *CoynV"4}.7LؕۈߡG.VLT 9M\ KoJoI+^+t uHwG EngGKz^NeLFL#`wN(SX#(7,%>iM^P+X}(e>\5evsmtĘU0 ̄ՠ:m@Dg˒]G,!ܳp8͑|Iń|GkJJr;⢢쪄#'7{¾^ }y2i Ny"z#Fv'bbV.|[Vn"* ;T8=4xst KL9hikҲkh^6v}3MUgPk'{iB#Sdcf>:sSުx.`lpwO"\ /rc=7H勖|%b#shzIj,׸Gm`4zGYdK"D޸ ͔&u>FÙBYɪ|{]ìf%V,]AI=Fchɏ5qQ[JK`|yNZmE lĐr1sFbCoLbߔ|pHgG)GT'^":BBA V"$`%܆@v9D` כHM6ZӪ 8[l{ɝd|V/,>` -ѡ7- BkyxH![lbՠوh48BLYV䭌˚on#M _t:֭+>1$ .oIwtU\^>cpgK`5?g` ި;b.M,[Yr1MK-h=Fmqo"y9Po"Fj1ӻR|Ieh9-Z3T,`EZR@)V kZ-@겴Efٻ1l-cԝ@9H[Og~sÏA6AuK"70$:_?(oVxcpKD@ hdĭSBlW,&;#"Wb LY8UCũiɔ.jȌ ^YDDPR2_KiMMØM'Ç6S4E]H0@^b?#q4Ɣہnj@c~kn (ޠdͬ S^lN\ByB-((8;EM(^KCc;r4!?N DCPr!%.;͆qV.ӎ21ś4)nt 2)4*xIa[Ghh&qjPBچQiM/nk}VEUlm*wb,^샓5J;{y0V-pMyōQ&eNFue 8Gn@5b \oAyrAxͦyân8YQ nQ#p: Q;)9|󐻛퇳a -ā5q* c!eF|J҆#-]3^ֱF"ssXD 8 'T59IߏC~Bb?m4p@/Med]ZgsC$G:{o= `6ͭ:񮐼Xa I^ nRr z%!o% ̤?_n; IzN&<p`kwb0K / . ~gbmB:$Td@/ by9q}N2ab0}OtWqoR’kT6M\[q8Ы0Mr󑥤;oᚠ4H)+7u 4h%dv4hAp{]pG)G0ǷNL_Jxբ'l'b};Mg~L{]8E+v qcՂ9 $` ٿ c/sګi#nAwx1L n\nFs/{I&YSOFf(Wzzx/ѷ_U@5W S=kP9֒K⮜"n4Cګ4q]ARI#"Y)1ǔbYZ*F]Ow<|gW {-^2G $3`|g~NXZ9Fj7km{Ps1\X DPj_l)= Aux\reG;b(RBM<6a0XY`i;Գ.bZn  SjR $@*Ѥ=)CNt|5+yI5%8q8mm_dd FXfn ;zCk[dT5ޯY\-"* ڔim9O#W)۵3lRF?TxիД%08q\a65P!j)}+㮽b.}%asQ'PGwcc5*2 j R\Uceڵ0y" f Z36PH)_y8kOW+yvǏe^UuG5QѽMY\)XkG;,;/%{U1]=>T9`(hʏz`%(uȫ/㟼OʮIC, N԰}Wu@6BX!Ba޼N6](+,2x0  [[4c!ޱ3(Z& Aע 4^R!E2_}̒i@ ϊo>|ms3<3;!J@#|S@ 1x@ b]UlYiʸ~ @WEU=(WmiCMwbhqZLGxWhf]}ukrs"toR]wy?H$)O*iO"ONA5պݹSe^u?0%R\=qڀ3U_r%&NGB s Io,<ȓ>kΓFeG"^Kd نh8 b{s.RP/Ppl~RھkT)3j' `s2"j7 t9x Ղjc+H?VH(RmZ?(yǓ׼+_3& $UMT<lF[v_L\Q)dٿYTElNsNwI8 wOTc.%zGWsI˙A-Ȍ T{}50)F9uEж29KȎ[)!Ɩwt29s yFφۖMPN\SHQ+5`Z G}X T`Cঀvm<tWi~9p9PCQn0L 7Bv(= W%sȔh ĽWb^:R1|brI|(^h= \"81aWO@t/#s>!v}:Rt\4Hv1!+ϯCz= REaCq4ZxyWUhYgbI~W+ԋv>"EPîI*FHLL>U~q(&o29cYnFuܗ1&/|h.Vuj{9Tw8P~s2&~OpEc~,P ɻc@6̢sF^XEW? V~Q|<7.4^ό\ @HؠkU@7r")ĉ#hwЮH!7 !ni";q3^B`G ]@VR-˛6)) sW ^zˠ U!R e78O KTOcmž`F_ZXvԃYqJ+\+YAvLa0rR).qZa?nw-v5JE?۵;lF`v#4ޏkrGl]|$. u/ 0+R⢃2gh-q3Ah%KSE%V]Q*$fUwyΙ*ۭ '\"UfcbJƫ>?®k`zhg0-{23}>h\{4oBkR *Xڕ ~PyR j4?cz@<3+pbhC;wŲerl$AnP\ONw"DrYu֦KnOt3&_*mk&ZSJ&_ l삎WiN]KAFkr/l[EʆUWMp@EHmb:s,=Ie$2*35uT?xJT{H&<<08`*%;KBke5~%Q'S5(~0R 4 3[l;W{h ]\ErPܥo 鐕KC$?!![G %g9VJDTDi5zZ|I*zBl,qxpmpNK+ߡϥ yhF-yt#U'NK1ذK\D3`OS`x2(Q(Η 3αuKyLV%MY\YY}\۱?4jD;K=1ڧ\ǭz l^=\!!#khbl&XIaC# (*  +ؤ/!=C-2+?zHAHAnOɪUs-!b;)4'g>+ $dEB!A|(Z@5$ul0 ;pʏpEe6eEpwjˍ N/(#Xsb9B2GH O.V1wqob'Y]E*qs0F~H*B |wF`6c,͋^D0qgs{pO䅪0EWlsU@gM0nOr=a[&̾,4?p41Mտ]jы-> )rLId-")cpL$h\mW sZ-=Ihudӱ-(\x ;Σ[izw^ҩ&T^‡Nh\H`8>Vr_ T7) ^FBx:O AqU@,EP) x d2>+Fi2,l˙Jj-f#_ĴLl!-;Vֺ;[عD>@8Րtl%7XTƣLd3l<VkC쐼#m2!2+<5nPAp/ՙ'$h}/H\ۃ@D 4 Mж|y{.~Ao* ,'.uNtov,V+V)Zu<&*/Q8ś̰t(?2a Cچbj&Q; [ Uo$i8 b  ^# ҋ +9&^ \ 57 w] N>}91#E & n a G 4Gv?D 'hu :\L ~E( vqڷX0[gd_ ^\aM \ ?>w[K ܦ_b2 8 *- \h- ڎ O( pV|Mu >2x@Ў:=  h *sAxz4 `d4xt eۺT e,w T\BqZW.}n t ~ y w,%T){2 7 ] 5Rob2jC 6T*E  ܤ ½ \U +u`hw' ;  ȇ.-i3- 6 gqF: , AX<A`cޡ& ""&У  6J@ 0: To v<A% Z9s0 9]n Na $ U$J QyR 3}(%ATV !nލ~k @ {HcZ} ׾z‹F{% ެ\ %% ѐnx 1Z +ry L $4qjW Rεq;Ϟx6RrLZR^V ] 8; LT vf 7R2U ?1}+`t1#( Q ^= # iwZ m6 *ڵ 6 g <1I R4A d H <l~ZMQ ӄqY_)MBld ͭ!  ( S @ |m {?=yc%` !E]U $x %X1" ʑ J d#"&x~`AlO )' z co= 2 ޑ uXZ}$Y(} F  _&Sv J z #C9p6 ES iP كaa  %!h {{']z0d&2}x k8qw,tXB| ckH7BrNu E3dpg^T{ %Z 2 " L #x.`=}aQ W~5yd cum@܁ *g a&4(UR%1/ASZ|WXv ^() ^WR8 ? E! iv a]{DD \ |) /Fl &7W1&S~ }Qw'zVNBt=g{ ,C%R_ 1{ T T"~;Ļ * g w i &>یJ^   r  # gK+ Za<P>m_[0O Qe.Cs `*- އS,v h 5t "opt$ g/h R#D W5~  ̦!^ 1Г V 2C  . )F@oQF"- D* Xp T Wq 8 "pY(R "%tGpZWR2LZH.I;zc 4, KRV-`p-'K $ W*": N " vV  ت $ [s 'aך d 0 c Km (يZ,@Z; dwj˶_o  &hiyQ̅d: V89j _Xz{ 4 y' KH };K? $ f/"qq_6r 1)y#x*ʳy  zEA cT @+) d T2 ){^& goFVPD3 {r NF f=sj arsj   ha  y=խ'Q  4d!wm zMnED}F^ J K X(). |s in$ 2,| wImZzc2e  ۸:\ S"vYJT ZԵ#k _r Ğ  ՛ a 3 '}# "?0| X  ) )I  k $F, A x& ;sJ ($y t ?* i#qE{ {'n <gH sX >DLT%_&  F. eEK_~Uf2Υis:8~]A^j4 > Mp;" @9 $\fl. صu YUU ԝ O12wYN& VC>^ O ǡw ܽo " h֌cG\ G1 =K ݘHk ;` M I33cAvr^ E>NKd#. ald N+ؙvEC\XT ~woUGR$Pj\  8b(h cxަ23zUB/4p›tx#()E' j:vB 0 33ewv O+ MqyaJ  գ }tfSkhy t+ ` U>|eߕ `^ э c ۯc˺s ܭ,E{ @ dy «  i82K&P  WT]97~`S+ z_@  z` ~UyZ"A  W ;~ d/ 3)GDõ 'Y kdo9- -@RQ ;q4d{ k`8 S#gu() nl5t+,dQ f`7 ) tZoڍNuċ Fz ;a>]F ѕfT/ 8' &ӟEV l> 'nn;noC[ !{z ) "cg"nL : `|4| Sw3͟ ` ;е[^vcR? JbN  ˩ 6#p )X7 z V!v; ݶVvzea G %]9SV\Q <` ] f3 kw 7\Gr{Ur J\^.Bա Ad[L biCځmf.oRv5=iprutils/.git/objects/info/0000755000000000000000000000000012275251105014654 5ustar rootrootiprutils/.git/HEAD0000644000000000000000000000003212275251164012714 0ustar rootrootref: refs/heads/rel-2-4-0 iprutils/Makefile0000644000000000000000000000535012275251164013077 0ustar rootroot# (C) Copyright 2000, 2001 # International Business Machines Corporation and others. # All Rights Reserved. This program and the accompanying # materials are made available under the terms of the # Common Public License v1.0 which accompanies this distribution. INCLUDEDIR = -I. -I/usr/include/ncurses CC = gcc include version.mk CFLAGS = -g -Wall $(IPR_DEFINES) UTILS_VER = $(IPR_MAJOR_RELEASE).$(IPR_MINOR_RELEASE).$(IPR_FIX_LEVEL) TAR = cd .. && tar -zcpf iprutils-$(UTILS_VER)-src.tgz --exclude CVS --exclude applied-patches --exclude series --exclude txt --exclude pc --exclude patches --exclude debug --exclude *~* iprutils all: iprconfig iprupdate iprdump iprinit iprdbg docs iprconfig: iprconfig.c iprlib.o iprconfig.h $(CC) $(CFLAGS) $(INCLUDEDIR) -o iprconfig iprconfig.c iprlib.o -lform -lpanel -lncurses -lmenu iprupdate: iprupdate.c iprlib.o $(CC) $(CFLAGS) $(INCLUDEDIR) -o iprupdate iprlib.o iprupdate.c iprdump:iprdump.c iprlib.o $(CC) $(CFLAGS) $(INCLUDEDIR) -o iprdump iprlib.o iprdump.c iprinit:iprinit.c iprlib.o $(CC) $(CFLAGS) $(INCLUDEDIR) -o iprinit iprlib.o iprinit.c iprdbg:iprdbg.c iprlib.o $(CC) $(CFLAGS) $(INCLUDEDIR) -o iprdbg iprlib.o iprdbg.c iprucode:iprucode.c iprlib.o $(CC) $(CFLAGS) $(INCLUDEDIR) -o iprucode iprlib.o iprucode.c iprlib.o: iprlib.c iprlib.h $(CC) $(CFLAGS) $(INCLUDEDIR) -o iprlib.o -c iprlib.c %.8.gz : %.8 gzip -f -c $< > $<.gz %.nroff : %.8 nroff -Tascii -man $< > $@ %.ps : %.nroff a2ps -B --columns=1 -R -o $@ $< %.pdf : %.ps ps2pdf $< $@ %.html : %.8 groff -Thtml -man $< > $@ docs : $(patsubst %.8,%.8.gz,$(wildcard *.8)) pdfs : $(patsubst %.8,%.pdf,$(wildcard *.8)) htmldocs : $(patsubst %.8,%.html,$(wildcard *.8)) utils: ./*.c ./*.h cd .. $(TAR) clean: rm -f iprupdate iprconfig iprdump iprinit iprdbg *.o rm -f *.ps *.pdf *.nroff *.gz *.tgz *.rpm *.html install: all install -d $(INSTALL_MOD_PATH)/sbin install --mode=755 iprconfig $(INSTALL_MOD_PATH)/sbin/iprconfig install --mode=755 iprupdate $(INSTALL_MOD_PATH)/sbin/iprupdate install --mode=755 iprdump $(INSTALL_MOD_PATH)/sbin/iprdump install --mode=755 iprinit $(INSTALL_MOD_PATH)/sbin/iprinit install --mode=700 iprdbg $(INSTALL_MOD_PATH)/sbin/iprdbg install -d $(INSTALL_MOD_PATH)/usr/share/man/man8 install iprconfig.8.gz $(INSTALL_MOD_PATH)/usr/share/man/man8/iprconfig.8.gz install iprupdate.8.gz $(INSTALL_MOD_PATH)/usr/share/man/man8/iprupdate.8.gz install iprdump.8.gz $(INSTALL_MOD_PATH)/usr/share/man/man8/iprdump.8.gz install iprinit.8.gz $(INSTALL_MOD_PATH)/usr/share/man/man8/iprinit.8.gz rpm: *.c *.h *.8 -make clean cd .. $(TAR) mv ../iprutils-$(UTILS_VER)-src.tgz . rpmbuild --nodeps -ts iprutils-$(UTILS_VER)-src.tgz cp `rpm --eval '%{_srcrpmdir}'`/iprutils-$(UTILS_VER)-$(IPR_RELEASE).src.rpm .